考虑以下类型类:
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
实例?如果没有,通常的解决方法是什么?
答案 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]