如何使元组成为 Haskell 中此 class 的实例?
How can I make tuples an instance of this class in Haskell?
我一直在读“What I wish I knew when learning 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
我的问题是:如何创建 class 的实例?这个想法是将函数调用为:
λ bimap (+1) (+2) (8, 9) -- (9, 11)
λ first (*4) (10, 8) -- (40, 8)
λ second (*2) (3, 5) -- (3, 10)
我最接近完成的是:
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)
但它不起作用,它引发了一个错误:
• 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)’
(x,y)
已经是一个具体元组类型,包含两个具体(虽然未知)类型 x
和 y
。同时,函子或双函子应该是 parametric,即在元组实例的情况下,您希望包含的类型作为参数保持打开状态,然后用使用方法时的各种不同的具体类型。
也就是说,你基本上想要一个类型级别的 lambda
instance Bifunctor (\x y -> (x, y)) where
好吧,Haskell 没有类型级别的 lambda,但它确实在类型级别有部分应用——在这种情况下甚至 partial,你根本不想将元组构造函数应用于任何类型 ,只是让它们保持打开状态。是这样写的:
instance Bifunctor (,) where
如果你只想将它应用于一个参数,你写
instance Functor ((,) a) where
如果解析为 Functor (a,)
我觉得更容易理解 – 但在 Haskell.
中这实际上是不合法的
好问题。
class 适用于仿函数类型本身,在您的例子中,仿函数类型是 (,)。要感受一下,请注意这里的区别。
:t (,)
(,) :: a -> b -> (a, b)
:t (True,False)
(True,False) :: (Bool, Bool)
如果您使用像这样的 Pair 类型,可能会更直观:
data Pair a b = Pair a b
因为阅读 class 定义会使 'p' 的类型应用更加明显。
就像 Haskell 对值使用类型一样,如上图所示,它对类型(也对编译时逻辑)使用类型,这些类型被命名为 Kinds。
:k Pair
Pair :: * -> * -> *
:k (,)
(,) :: * -> * -> *
:k (Bool,Bool)
(Bool,Bool) :: *
:k Bifunctor
Bifunctor :: (* -> * -> *) -> Constraint
最后一行说明 Bifunctor class 是为 (* -> * -> *)
类型设计的,而不是 (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)
编辑:根据@leftroundabout
建议的种类的说明
我一直在读“What I wish I knew when learning 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
我的问题是:如何创建 class 的实例?这个想法是将函数调用为:
λ bimap (+1) (+2) (8, 9) -- (9, 11)
λ first (*4) (10, 8) -- (40, 8)
λ second (*2) (3, 5) -- (3, 10)
我最接近完成的是:
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)
但它不起作用,它引发了一个错误:
• 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)’
(x,y)
已经是一个具体元组类型,包含两个具体(虽然未知)类型 x
和 y
。同时,函子或双函子应该是 parametric,即在元组实例的情况下,您希望包含的类型作为参数保持打开状态,然后用使用方法时的各种不同的具体类型。
也就是说,你基本上想要一个类型级别的 lambda
instance Bifunctor (\x y -> (x, y)) where
好吧,Haskell 没有类型级别的 lambda,但它确实在类型级别有部分应用——在这种情况下甚至 partial,你根本不想将元组构造函数应用于任何类型 ,只是让它们保持打开状态。是这样写的:
instance Bifunctor (,) where
如果你只想将它应用于一个参数,你写
instance Functor ((,) a) where
如果解析为 Functor (a,)
我觉得更容易理解 – 但在 Haskell.
好问题。
class 适用于仿函数类型本身,在您的例子中,仿函数类型是 (,)。要感受一下,请注意这里的区别。
:t (,)
(,) :: a -> b -> (a, b)
:t (True,False)
(True,False) :: (Bool, Bool)
如果您使用像这样的 Pair 类型,可能会更直观:
data Pair a b = Pair a b
因为阅读 class 定义会使 'p' 的类型应用更加明显。
就像 Haskell 对值使用类型一样,如上图所示,它对类型(也对编译时逻辑)使用类型,这些类型被命名为 Kinds。
:k Pair
Pair :: * -> * -> *
:k (,)
(,) :: * -> * -> *
:k (Bool,Bool)
(Bool,Bool) :: *
:k Bifunctor
Bifunctor :: (* -> * -> *) -> Constraint
最后一行说明 Bifunctor class 是为 (* -> * -> *)
类型设计的,而不是 (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)
编辑:根据@leftroundabout
建议的种类的说明