我有两个haskell函数,它们在两个代数数据类型之间进行转换。
data Ab = A | B
data Cd = C | D
fromAb :: Ab -> Cd
fromAb A = C
fromAb B = D
toAb :: Cd -> Ab
toAb C = A
toAb D = B
但是我想创建一个多态函数,它接受代数数据类型并在它们之间进行转换。
foo A = C
foo B = D
foo C = A
foo D = B
但是Haskell从" foo A = C"该功能是
foo :: Ab -> Cd
我试图让类的数据类型实例变成foo polymorph,但它没有用。
class Abcd a
instance Abcd Ab
instance Abcd Cd
foo :: Abcd a => a -> Ab
任何想法?
答案 0 :(得分:14)
TypeFamilies
这是非常自然的。您定义了一个类型级函数
type family Converted a
type instance Converted Ab = Cd
type instance Converted Cd = Ab
然后你的签名变为
foo :: a -> Converted a
如果你只是在摆弄类型,那么你可以完成,但由于你想在价值层面上有不同的行为(从A
返回C
等等),我们实际上需要在新类型的实例之间传播我们的案例:
class Convertable a where
foo :: a -> Converted a
instance Convertable Ab where
foo A = C
foo B = D
instance Convertable Cd where
foo C = A
foo D = B
最后,如果使用最近的GHC,您可以考虑将Converted
设为封闭类型的同义词系列,或者将其设为"关联的"通过移动Convertable
实例声明中的实例。
答案 1 :(得分:4)
嗯,你上一个代码片段中的签名仍然是错误的。它不会是foo :: Abcd a => a -> Ab
,因为如果a ~ Ab
那么函数应该返回Cd
,而不是Ab
。
有几种不同的方法可以做你想做的事。首先,要认识到你要做的是基于一种类型而不是两种类型之间的关系来表达一组共同的行为。这基本上是多参数类型类的目的(这可能是实现此目的的最简单方法)。
{-# LANGUAGE MultiParamTypeClasses #-}
data Ab = A | B
data Cd = C | D
fromAb :: Ab -> Cd
fromAb A = C
fromAb B = D
toAb :: Cd -> Ab
toAb C = A
toAb D = B
class Iso a b where
to :: a -> b
instance Iso Ab Cd where
to = fromAb
instance Iso Cd Ab where
to = toAb
编辑:请注意,我的答案完全等同于使用类型系列的jberryman。这就是我所说的“做你想做的几种方式”。
答案 2 :(得分:1)
另一种方法是使用扩展程序MultiParamTypeClasses
和FunctionalDependencies
:
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
data Ab = A | B deriving (Show)
data Cd = C | D deriving (Show)
class Convert a b | a -> b where
convert :: a -> b
instance Convert Ab Cd where
convert A = C
convert B = D
instance Convert Cd Ab where
convert C = A
convert D = B
演示:
λ> convert A
C
λ> convert B
D
λ> convert C
A
λ> convert D
B