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

class SerDes a where
    ser :: a -> ByteString
    des :: ByteString -> a
事实证明,为每种类型的a(例如,
compress :: ByteString -> ByteString     -- actually varies with the original type
我将compress视为要​​与每个aSerDes关联的函数。 (“关联”一词可能是一个错误的选择,也是互联网搜索无法产生任何结果的原因。)
该示例并不像它看起来的那样虚构,例如当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
并将compressa实例化为
compress (For :: For MyType) input = ...
另一种不同寻常的方法是将所有功能记录在案。
data SerDes a = SerDes { ser      :: a -> ByteString
                       , des      :: ByteString -> a
                       , compress :: ByteString -> ByteString
                       }
还有其他方法可以将compress函数与a类型“关联”吗?

最佳答案

您的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)来告诉编译器我们想要compressT函数。

就个人而言,我不喜欢代理和标签。过去,GHC不允许使用其他更简单的解决方案时就需要使用它们,但是现在不应该再使用它们。
现代的方法是打开无害扩展AllowAmbiguousTypesTypeApplications并简单地编写所需的类
class SerDes a where
    ser      :: a -> ByteString
    des      :: ByteString -> a
    compress :: ByteString -> ByteString
在这种方法中,代替调用compress (Proxy :: Proxy T) someByteString,我们需要使用较短的compress @T someByteString,在这里我们明确地“传递所需的a类型”(在这种情况下为T),以便选择所需的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 - 将函数与Haskell中的类型相关联,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/63667281/

10-10 16:26