Function 用于在代数数据类型之间转换的Haskell多态函数

Function 用于在代数数据类型之间转换的Haskell多态函数,function,haskell,types,Function,Haskell,Types,我有两个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从“fooa=C”推断出函

我有两个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从“fooa=C”推断出函数是

foo :: Ab -> Cd
我试图创建一个类的数据类型实例来创建foo polymorph,但没有成功

class Abcd a
instance Abcd Ab
instance Abcd Cd

foo :: Abcd a => a -> Ab

有什么想法吗?

这对于
TypeFamilies
来说是很自然的。您可以定义类型级别的函数

type family Converted a
type instance Converted Ab = Cd
type instance Converted Cd = Ab
那么你的签名就变成了

foo :: a -> Converted a
如果您只是在摆弄类型,那么您就可以这样做了,但是由于您希望在值级别上有不同的行为(从A
C
返回A,等等),我们实际上需要在新类型类的实例中分布我们的案例:

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,可以考虑将<代码>转换为一个封闭类型的同义词族,或者通过在可转换的实例声明中移动实例来使它“关联”。

< P>您的上一个代码片段中的签名仍然是错误的。它不是
foo::abo a=>a->Ab
,因为如果
a~Ab
,那么函数应该返回一张
Cd
,而不是
Ab

做你想做的事有几种不同的方法。首先,要认识到,您试图表达的是一组常见的行为,而不是基于一个类型,而是基于两个类型之间的关系。这基本上就是多参数typeclass的用途(这可能是实现这一点的最简单方法)


编辑:请注意,我的答案与jberryman的完全相同,后者使用类型族。这就是我所说的“做你想做的事情的几种方法”的意思。

另一种方法是使用扩展名
MultiParamTypeClasses
函数依赖性

{-# 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
演示:


Fundeps可能是op想要的:
class Iso a b | a->b
,这将是解决此问题的传统方法(预类型族)。对于这种情况,实际上不需要Fundeps,因为
to
使用两种类型的实例。这将阻止您向此“可转换”类型组添加新类型。抱歉,我的评论不太清楚。我的意思是,fundeps的类型推断会更好,要么
a->b
(相当于TypeFamilies),要么
a->b,b->a
。我相信,将
写入一个
中是不明确的。不,你是对的,添加fundeps当然没有错,但它表达的属性可能是可取的,也可能是不可取的,这取决于应用程序。如果我们稍后添加
data Ef
,我们没有理由不能通过声明适当的实例,使用
在3种类型之间任意转换为
。但是,如果我们使用fundep,这是不可能的,但是如果我们不需要这种灵活性,当然还有更简单的类型推断的额外好处。
{-# 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