如何为任一类型创建Functor定义

时间:2016-04-22 12:54:13

标签: haskell

我是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" ?

4 个答案:

答案 0 :(得分:6)

正如您对previous question的回答中所述,仿函数需要一种类型* -> *Either1* -> * -> *种;你需要部分应用Either1来获得一种合适的类型。

> :k Either1
Either1 :: * -> * -> *
> :k Either1 Int
Either1 Int :: * -> *

在定义Functor实例时,第一种类型对于仿函数并不重要,因此您只需指定一个无约束的类型变量而不是像Int这样的具体类型

答案 1 :(得分:2)

实现Functor时,需要一个接受单一类型参数的类型。 Either1接受两个类型参数ab,因此您的定义应如下所示:

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 - 所以我迫切需要有人给我简单的解释。

祝你学习好运!