NrB*_*ex 13 haskell types typeclass type-kinds
我一直在阅读“我希望在学习 Haskell 时知道什么”这本书,然后我停在了这个例子上:
class Bifunctor p where
bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
first :: (a -> b) -> p a c -> p b c
second :: (b -> c) -> p a b -> p a c
Run Code Online (Sandbox Code Playgroud)
我的问题是:如何创建该类的实例?这个想法是将函数调用为:
? bimap (+1) (+2) (8, 9) -- (9, 11)
? first (*4) (10, 8) -- (40, 8)
? second (*2) (3, 5) -- (3, 10)
Run Code Online (Sandbox Code Playgroud)
我最接近做到这一点的是:
instance Bifunctor (x, y) where
bimap func func' (x, y) = (func x, func' y)
first func (x, y) = (func x, y)
second func (x, y) = (x, func y)
Run Code Online (Sandbox Code Playgroud)
但它不起作用,它引发了一个错误:
• Expecting two fewer arguments to ‘(x, y)’
Expected kind ‘* -> * -> *’, but ‘(x, y)’ has kind ‘*’
• In the first argument of ‘Bifunctor’, namely ‘(x, y)’
In the instance declaration for ‘Bifunctor (x, y)’
Run Code Online (Sandbox Code Playgroud)
Pau*_*l R 13
好问题。
该类适用于函子类型本身,在您的情况下,函子类型是 (,)。要了解它,请注意此处的差异。
:t (,)
(,) :: a -> b -> (a, b)
:t (True,False)
(True,False) :: (Bool, Bool)
Run Code Online (Sandbox Code Playgroud)
如果您使用像这样的 Pair 类型,它可能会更直观:
data Pair a b = Pair a b
Run Code Online (Sandbox Code Playgroud)
因为阅读类定义会使'p'的类型应用更加明显。
就像 Haskell 使用类型作为值一样,如上所示,它使用类型作为类型(也用于编译时逻辑),称为Kinds。
:k Pair
Pair :: * -> * -> *
:k (,)
(,) :: * -> * -> *
:k (Bool,Bool)
(Bool,Bool) :: *
:k Bifunctor
Bifunctor :: (* -> * -> *) -> Constraint
Run Code Online (Sandbox Code Playgroud)
最后一行说明 Bifunctor 类是为 kind 类型设计的(* -> * -> *),而不是(*)为 (a,b)类型设计的,因此您从 GHC 获得了错误消息。
你的定义几乎是正确的,这是正确的:
instance Bifunctor (,) where
bimap func func' (x, y) = (func x, func' y)
first func (x, y) = (func x, y)
second func (x, y) = (x, func y)
Run Code Online (Sandbox Code Playgroud)
编辑:插图种由@leftroundabout建议
(x,y)已经是一个具体的元组类型,包含两个具体的(尽管未知)类型x和y. 同时,函子或双函子应该是参数化的,即在元组实例的情况下,您希望包含的类型作为参数保持打开状态,然后在使用方法时用各种不同的具体类型填充。
即,您基本上想要一个类型级别的 lambda
instance Bifunctor (\x y -> (x, y)) where
Run Code Online (Sandbox Code Playgroud)
好吧,Haskell 没有类型级别的 lambdas,但它确实在类型级别有部分应用——在这种情况下甚至不是partial,你根本不想将元组构造函数应用于任何类型,而只是让它们保持打开状态. 是这样写的:
instance Bifunctor (,) where
Run Code Online (Sandbox Code Playgroud)
如果你只想将它应用于一个参数,你可以写
instance Functor ((,) a) where
Run Code Online (Sandbox Code Playgroud)
如果将其解析为,我发现更容易理解Functor (a,)- 但这在 Haskell 中实际上并不合法。