当类型类只有一个时,具有2个参数的类型的类型类实例

时间:2011-07-19 12:26:53

标签: haskell typeclass

考虑以下类型类:

class Listable a where
   asList :: a t -> [t]

使用一个参数为类型创建实例很容易:

instance Listable [] where
   asList = id

instance Listable Maybe where
   asList (Just x) = [x]
   asList Nothing = []

现在,如何为具有两个相同类型参数的对创建实例?当然,我可以做一些包装:

data V2 a = V2 a a

v2 (p,q) = V2 p q

instance Listable V2 where
   asList (V2 p q) = [p,q]

现在我可以写asList $ v2 (47, 11)之类的东西了,但这种方式会失败。

有没有办法将对的类型限制为两个类型参数相等的情况,并为此编写Listable实例?如果没有,通常的解决方法是什么?

2 个答案:

答案 0 :(得分:13)

从概念上讲,有很多方法可以做到这一点。不幸的是,他们中的大多数实际上并不工作。唉!

首先,作为一名功能性程序员,我打赌这是你真正想要写的东西:

instance Listable (\a -> (a, a)) where
    asList (p, q) = [p,q]

不幸的是,类型级别的lambda不存在。我们可以使用类型同义词编写上述lambda的命名版本:

type Same2 f a = f a a

instance Listable (Same2 (,)) where { ... }

这也是不允许的,因为类型同义词未完全应用。我们可以设想类型类采用额外的参数来描述如何应用类型变量:

class Listable app f where
    asList :: app f a -> [a]

instance Listable __ Maybe where { ... }

instance Listable __ (,) where { ... }

如果不考虑app可能是什么,这也会失败,因为f参数没有一致的类型。

继续进行实际工作的事情,我认为最常见的方法是将类型同义词方法包含在newtype中,然后处理涉及的包装和解包。

newtype Same2 f a = Same2 (f a a)

instance Listable (Same2 (,)) where { ... }

如果有点难看,这是可行的。你也可以用这种方式定义类型构造函数组合和其他玩具,然后坚持使用类型级无点表达式埋在一堆箍跳板下。

作为最后一种方法,您还可以对“反向”上方的lambda样式方法进行编码,从完全应用的版本单一类型参数:

class Listable t where
    type ListableElem t :: *
    asList :: t -> [ListableElem t]

能够做这类事情是类型家庭的主要动机之一。同样的事情可以用MPTC和fundeps表达,但它是1)相当于2)更加丑陋,所以我不打算写出来。

答案 1 :(得分:2)

您必须定义一些包装器类型才能执行此操作。 (但是你应该使用newtype。)你甚至可以定义:

newtype Foo t a = Foo(t a a)

instance Listable (Foo (,)) where
   asList (Foo (a,b)) = [a,b]