问题描述
假设您有一个序列化器/反序列化器类型类
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
强制添加到参数之外,还可以使用a
将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)
来告诉编译器我们想要compress
的{{1}}函数。
就个人而言,我不喜欢代理和标签。过去,GHC不允许使用其他更简单的解决方案时才需要使用它们,但现在不应该再使用它们。
现代的方法是打开无害扩展T
和AllowAmbiguousTypes
,然后简单地编写所需的类
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