将函数与Haskell中的类型相关联 [英] Associate a function with a type in Haskell

查看:81
本文介绍了将函数与Haskell中的类型相关联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设您有一个序列化器/反序列化器类型类

Suppose you have a serializer/deserializer type class

class SerDes a where
    ser :: a -> ByteString
    des :: ByteString -> a

事实证明,对于每种类型a,例如

and it turns out that it's crucial to have a special helper function for each type a, e.g.

compress :: ByteString -> ByteString     -- actually varies with the original type

我将compress视为我想与作为SerDes的每个a关联的功能. (关联"一词可能是一个错误的选择,这也是互联网搜索无法产生任何结果的原因.)

I see compress as a function that I would like to associate with each a that is a SerDes. (The word "associate" is probably a bad choice, and the reason why internet searches yield nothing.)

该示例并不像它看起来的那样虚构,例如,当decompress是可选的时 串行器/解串器的功能. (是的,可以通过增加 ser,带有一个控制压缩的开关,ser:: a -> Bool -> ByteString,或者更好地使用Config记录.但是,让我们坚持这个例子.)

The example is not as contrived as it looks, for example when decompress is an optional feature of the serializer/deserializer. (Yes, the helper could be avoided by augmenting ser with a switch that controls the compression, ser:: a -> Bool -> ByteString, or better use a Config record. But let's stick with the example.)

一种实现此目的的方法是虚拟"类,即单例:

One way to do this is a 'dummy' class, a singleton:

data For a = For

然后这将起作用:

class SerDes a where
    ser      :: a -> ByteString
    des      :: ByteString -> a
    compress :: For a -> ByteString -> ByteString

acompress将被实例化为

compress (For :: For MyType) input = ...

另一种有点不寻常的方法是将所有功能都保留在记录中.

Another way, somewhat unusual, would be to stick all the functions in a record.

data SerDes a = SerDes { ser      :: a -> ByteString
                       , des      :: ByteString -> a
                       , compress :: ByteString -> ByteString 
                       }

是否还有其他关联"方式? compress函数的类型为a?

Are there any other ways to "associate" the compress function with the type a?

推荐答案

您的For a类型在库中称为Proxy a.

import Data.Proxy

class SerDes a where
    ser      :: a -> ByteString
    des      :: ByteString -> a
    compress :: Proxy a -> ByteString -> ByteString

有时,它通常被泛化为通用的proxy类型变量.

Sometimes this is generalized to a generic proxy type variable.

class SerDes a where
    ser      :: a -> ByteString
    des      :: ByteString -> a
    compress :: proxy a -> ByteString -> ByteString


还有另一种选择,类似于代理.除了强制将a添加到参数之外,还可以使用Taggeda添加到结果类型:


There is another option, similar to proxies. Instead of forcibly adding a to the arguments, one can add a to the result type using Tagged:

import Data.Tagged

class SerDes a where
    ser      :: a -> ByteString
    des      :: ByteString -> a
    compress :: ByteString -> Tagged a ByteString

这需要用作unTagged (compress someByteString :: Tagged T ByteString)来告诉编译器我们想要Tcompress函数.

This needs to be used as unTagged (compress someByteString :: Tagged T ByteString) to tell the compiler we want the compress function for T.

我个人不喜欢代理和标签.过去,GHC不允许使用其他更简单的解决方案时就需要使用它们,但现在不应该再使用它们.

Personally, I'm not a fan of proxies and tags. They were needed in the past when GHC did not allow another simpler solution, but right now they should no longer be used.

现代的方法是打开无害的扩展AllowAmbiguousTypesTypeApplications并只需编写您想要的类

The modern approach is to turn on the harmless extensions AllowAmbiguousTypes and TypeApplications and simply write your wanted class

class SerDes a where
    ser      :: a -> ByteString
    des      :: ByteString -> a
    compress :: ByteString -> ByteString

在这种方法中,代替调用compress (Proxy :: Proxy T) someByteString,我们将需要使用较短的compress @T someByteString,在此我们显式地传递所需的类型a". (在这种情况下为T),因此选择所需的compress.

In this approach, instead of calling compress (Proxy :: Proxy T) someByteString we will need to use the shorter compress @T someByteString where we explicitly "pass the type a we want" (T in this case), so to select the wanted compress.

完整示例:

{-# LANGUAGE AllowAmbiguousTypes, TypeApplications, OverloadedStrings #-}

import Data.ByteString as BS

class SerDes a where
    ser      :: a -> ByteString
    des      :: ByteString -> a
    compress :: ByteString -> ByteString

-- bogus implementation to show everything type checks
instance SerDes Int where
   ser _ = "int"
   des _ = 42
   compress bs = BS.tail bs

-- bogus implementation to show everything type checks
instance SerDes Bool where
   ser _ = "bool"
   des _ = True
   compress bs = bs <> bs

main :: IO ()
main = BS.putStrLn (compress @Int "hello" <> compress @Bool "world")
-- output: elloworldworld

这篇关于将函数与Haskell中的类型相关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆