考虑以下类型类:
class Listable a where
asList :: a t -> [t]
Run Code Online (Sandbox Code Playgroud)
使用一个参数为类型创建实例很容易:
instance Listable [] where
asList = id
instance Listable Maybe where
asList (Just x) = [x]
asList Nothing = []
Run Code Online (Sandbox Code Playgroud)
现在,我将如何为具有两个相同类型参数的对创建实例?当然我可以做一些包装:
data V2 a = V2 a a
v2 (p,q) = V2 p q
instance Listable V2 where
asList (V2 p q) = [p,q]
Run Code Online (Sandbox Code Playgroud)
现在我可以写出像这样的东西asList $ v2 (47, 11),但这种方式会失败.
有没有办法将对的类型限制为两个类型参数相等的情况,并为此编写Listable实例?如果没有,通常的解决方法是什么?
C. *_*ann 13
从概念上讲,有很多方法可以做到这一点.不幸的是,他们中的大多数实际上并不工作.唉!
首先,作为一名功能性程序员,我打赌这是你真正想写的:
instance Listable (\a -> (a, a)) where
asList (p, q) = [p,q]
Run Code Online (Sandbox Code Playgroud)
不幸的是,类型级lambda不存在.我们可以使用类型同义词编写上述lambda的命名版本:
type Same2 f a = f a a
instance Listable (Same2 (,)) where { ... }
Run Code Online (Sandbox Code Playgroud)
这也是不允许的,因为类型同义词没有完全应用.我们可以设想类型类采用额外的参数来描述如何应用类型变量:
class Listable app f where
asList :: app f a -> [a]
instance Listable __ Maybe where { ... }
instance Listable __ (,) where { ... }
Run Code Online (Sandbox Code Playgroud)
甚至没有考虑app可能是什么,这也失败了,因为我们没有一致的f参数.
转到实际工作的东西,我认为最常见的方法是在a中包含类型同义词方法newtype,然后只处理涉及的包装和解包.
newtype Same2 f a = Same2 (f a a)
instance Listable (Same2 (,)) where { ... }
Run Code Online (Sandbox Code Playgroud)
如果有点难看,这是可行的.您也可以通过这种方式定义类型构造函数组合和其他玩具,然后坚持使用类型级无点表达式隐藏在一堆跳环跳板下.
作为最后的方法,您还可以将"反向"上面的lambda样式方法编码,从完全应用的版本到单一类型参数:
class Listable t where
type ListableElem t :: *
asList :: t -> [ListableElem t]
Run Code Online (Sandbox Code Playgroud)
能够做这种事情是类型家庭的主要动机之一.同样的事情可以用MPTC和fundeps表达,但它是1)相当于2)更加丑陋,所以我不打算写出来.