我是Haskell的新手,试图了解Functor。我从Data.Either模块中获取了以下代码(用Either1替换数据)。我刚刚更新了删除' Either1 a'并将其替换为' Either1' (实例Functor Either1 其中)。
data Either1 a b = Left1 a | Right1 b
instance Functor Either1 where
fmap f (Left1 x) = Left1 x
fmap f (Right1 y) = Right1 (f y)
当我尝试加载上面的代码段时,我收到了以下错误。
Prelude> :load Sample.hs
[1 of 1] Compiling Main ( Sample.hs, interpreted )
Sample.hs:3:18:
Expecting one more argument to ‘Either1’
The first argument of ‘Functor’ should have kind ‘* -> *’,
but ‘Either1’ has kind ‘* -> * -> *’
In the instance declaration for ‘Functor Either1’
Failed, modules loaded: none.
我的问题是,我为什么要把" Either1 a"在定义fmap函数时,为什么不能' Either1" ?
答案 0 :(得分:6)
正如您对previous question的回答中所述,仿函数需要一种类型* -> *
。 Either1
有* -> * -> *
种;你需要部分应用Either1
来获得一种合适的类型。
> :k Either1
Either1 :: * -> * -> *
> :k Either1 Int
Either1 Int :: * -> *
在定义Functor
实例时,第一种类型对于仿函数并不重要,因此您只需指定一个无约束的类型变量而不是像Int
这样的具体类型
答案 1 :(得分:2)
实现Functor时,需要一个接受单一类型参数的类型。 Either1
接受两个类型参数a
和b
,因此您的定义应如下所示:
instance Functor (Either1 a) where
fmap f (Left1 x) = Left1 x
fmap f (Right1 y) = Right1 (f y)
(我还修复了Left1
案例中的编译错误;您错过了f
参数)
在线图书Learn You a Haskell对Functors进行了很好的介绍,详细介绍了此处所述Either
的部分应用。
答案 2 :(得分:1)
你是完全正确的 - 让functor实例仅在Either
的正确参数上工作有点奇怪。
确实Either
不仅仅是一个仿函数,而是一个bifunctor:
instance Bifunctor Either where
bimap f _ (Left a) = Left (f a)
bimap _ g (Right b) = Right (g b)
但是,你可能总是认为一个bifunctor也是两个functor参数之一的“monofunctor”。由于类型参数curry如何工作,你必须在Haskell中选择第二个。从本质上讲,你正在与“一半的bifunctor”合作!例如,
type EitherStr = Either String -- the `Right` argument is left open as the functor argument!
instance Functor EitherStr where
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)
但当然这适用于任何给定类型,而不仅仅是String
。所以你可以在第一个参数上使实例通用:
∀ a . instance Functor (Either a) where
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)
现在,实例之前的∀ a .
只是隐含在Haskell中。如果你想明确这是正确的语法:
{-# LANGUAGE ScopedTypeVariables, UnicodeSyntax #-}
instance ∀ a . Functor (Either a) where
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)
答案 3 :(得分:1)
在Learn You A Haskell,当您第一次见到期限' kind'你可以发现你不需要彻底了解这个概念继续前进,但在我看来你应该花一些时间来选择这个想法,因为如果没有好的话,更多的材料会变得越来越棘手理解这个问题。
如上所述,Either有类* -> * -> *
,但Functor期望类似* -> *
的东西 - 你可能会认为它是一个类型,用另一个(具体)类型参数化 - 实际 - 类型构造函数。例如,Int有*
种类,Maybe Int
种类*
,但Maybe
有种* -> *
- 所以类型构造函数Maybe
是好的在Functor中使用的候选人。我们如何使用Either a b
实现相同类型 - 再次使用部分应用程序来修复第一个参数,然后获得Either a
- 由一个具体类型b
参数化的构造。在这里你应该看到,为什么在fmap
函数中没有改变第一个参数 - 这是固定的。
我为第一个答案的某些可能重复道歉,但我记得自己花了太多时间并试图找不到kinds
- 所以我迫切需要有人给我简单的解释。