将函数与Haskell中的类型相关联

问题描述

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

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

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

,具有特殊的辅助功能至关重要
compress :: ByteString -> ByteString     -- actually varies with the original type

我将compress看作是一个函数,希望与每个a的{​​{1}}相关联。 (“关联”一词可能是一个错误的选择,这也是互联网搜索无法产生任何结果的原因。)

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

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

Config

这将起作用:

data For a = For

,并且class SerDes a where ser :: a -> ByteString des :: ByteString -> a compress :: For a -> ByteString -> ByteString 的{​​{1}}将被实例化为

compress

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

a

是否还有其他方法可以将compress (For :: For MyType) input = ... 函数与类型data SerDes a = SerDes { ser :: a -> ByteString,des :: ByteString -> a,compress :: ByteString -> ByteString } “关联”?

解决方法

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

import Data.Proxy

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

有时,它会泛化为通用的proxy类型变量。

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

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

import Data.Tagged

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

这需要用作unTagged (compress someByteString :: Tagged T ByteString)来告诉编译器我们想要compress的{​​{1}}函数。


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

现代的方法是打开无害扩展TAllowAmbiguousTypes,然后简单地编写所需的类

TypeApplications

在这种方法中,我们需要使用较短的class SerDes a where ser :: a -> ByteString des :: ByteString -> a compress :: ByteString -> ByteString ,而不是调用compress (Proxy :: Proxy T) someByteString,在此处明确“传递所需的类型compress @T someByteString”(a大小写),因此选择所需的T

完整示例:

compress