将函数与 Haskell 中的类型相关联
Associate a function with a type in Haskell
假设您有一个 serializer/deserializer 类型 class
class SerDes a where
ser :: a -> ByteString
des :: ByteString -> a
事实证明,为每种类型设置一个特殊的辅助函数至关重要 a
,例如
compress :: ByteString -> ByteString -- actually varies with the original type
我将 compress
视为一个函数,我想将其与每个 a
(即 SerDes
)相关联。 (“同事”这个词可能是一个错误的选择,也是互联网搜索一无所获的原因。)
这个例子并不像看起来那么做作,例如当 decompress
是可选的
serializer/deserializer 的特征。 (是的,可以通过增加
ser
带有控制压缩的开关,ser:: a -> Bool -> ByteString
,或者更好地使用 Config
记录。但让我们坚持这个例子。)
一种方法是 'dummy' class,单例:
data For a = For
那么这将起作用:
class SerDes a where
ser :: a -> ByteString
des :: ByteString -> a
compress :: For a -> ByteString -> ByteString
和 a
的 compress
将被实例化为
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
还有一个选项,类似于代理。可以使用 Tagged
:
将 a
添加到结果类型,而不是强行将 a
添加到参数中
import Data.Tagged
class SerDes a where
ser :: a -> ByteString
des :: ByteString -> a
compress :: ByteString -> Tagged a ByteString
这需要用作 unTagged (compress someByteString :: Tagged T ByteString)
来告诉编译器我们想要 T
的 compress
函数。
就个人而言,我不喜欢代理和标签。过去当 GHC 不允许另一个更简单的解决方案时需要它们,但现在不应再使用它们。
现代方法是打开无害的扩展 AllowAmbiguousTypes
和 TypeApplications
并简单地写下你想要的 class
class SerDes a where
ser :: a -> ByteString
des :: ByteString -> a
compress :: ByteString -> ByteString
在这种方法中,我们需要使用更短的 compress @T someByteString
而不是调用 compress (Proxy :: Proxy T) someByteString
,我们明确地“传递我们想要的类型 a
”(T
在这种情况下),所以 select 想要的 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
假设您有一个 serializer/deserializer 类型 class
class SerDes a where
ser :: a -> ByteString
des :: ByteString -> a
事实证明,为每种类型设置一个特殊的辅助函数至关重要 a
,例如
compress :: ByteString -> ByteString -- actually varies with the original type
我将 compress
视为一个函数,我想将其与每个 a
(即 SerDes
)相关联。 (“同事”这个词可能是一个错误的选择,也是互联网搜索一无所获的原因。)
这个例子并不像看起来那么做作,例如当 decompress
是可选的
serializer/deserializer 的特征。 (是的,可以通过增加
ser
带有控制压缩的开关,ser:: a -> Bool -> ByteString
,或者更好地使用 Config
记录。但让我们坚持这个例子。)
一种方法是 'dummy' class,单例:
data For a = For
那么这将起作用:
class SerDes a where
ser :: a -> ByteString
des :: ByteString -> a
compress :: For a -> ByteString -> ByteString
和 a
的 compress
将被实例化为
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
还有一个选项,类似于代理。可以使用 Tagged
:
a
添加到结果类型,而不是强行将 a
添加到参数中
import Data.Tagged
class SerDes a where
ser :: a -> ByteString
des :: ByteString -> a
compress :: ByteString -> Tagged a ByteString
这需要用作 unTagged (compress someByteString :: Tagged T ByteString)
来告诉编译器我们想要 T
的 compress
函数。
就个人而言,我不喜欢代理和标签。过去当 GHC 不允许另一个更简单的解决方案时需要它们,但现在不应再使用它们。
现代方法是打开无害的扩展 AllowAmbiguousTypes
和 TypeApplications
并简单地写下你想要的 class
class SerDes a where
ser :: a -> ByteString
des :: ByteString -> a
compress :: ByteString -> ByteString
在这种方法中,我们需要使用更短的 compress @T someByteString
而不是调用 compress (Proxy :: Proxy T) someByteString
,我们明确地“传递我们想要的类型 a
”(T
在这种情况下),所以 select 想要的 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