定义自己的Functor

IDo*_*OOP 2 haskell types functor instances

我一直试图理解Funask在Haskell中的含义,为此我想要一个Functor的例子,没有任何其他属性.

我想出的工作实例是

data MyFunctor a b = MyFunctor a b deriving Show
instance Functor (MyFunctor a) where
  fmap g (MyFunctor a b) = MyFunctor a $ g (b)
Run Code Online (Sandbox Code Playgroud)

我猜这是一个半实用的Functor,因为左值可以安全地存储,而正确的值操作.然后我希望在操作之前将左值替换为正确的值.以便...

Main> fmap (+2) MyFunctor 2 5
MyFunctor 5 7
Run Code Online (Sandbox Code Playgroud)

但是,更改实例声明以执行此操作会产生错误.

data MyFunctor a b = MyFunctor a b deriving Show
instance Functor (MyFunctor a) where
  fmap g (MyFunctor a b) = MyFunctor b $ g (b)
Run Code Online (Sandbox Code Playgroud)

我不明白,或知道搜索什么找到一个类似的足够的问题来帮助我.

C:\Haskell\func.hs:3:28: error:
    * Couldn't match type `a1' with `a'
      `a1' is a rigid type variable bound by
        the type signature for:
          fmap :: forall a1 b. (a1 -> b) -> MyFunctor a a1 -> MyFunctor a b
        at C:\Haskell\func.hs:3:3
      `a' is a rigid type variable bound by
        the instance declaration at C:\Haskell\func.hs:2:10
      Expected type: MyFunctor a b
        Actual type: MyFunctor a1 b
    * In the expression: MyFunctor b $ g (b)
      In an equation for `fmap':
          fmap g (MyFunctor a b) = MyFunctor b $ g (b)
      In the instance declaration for `Functor (MyFunctor a)'
    * Relevant bindings include
        b :: a1 (bound at C:\Haskell\func.hs:3:23)
        a :: a (bound at C:\Haskell\func.hs:3:21)
        g :: a1 -> b (bound at C:\Haskell\func.hs:3:8)
        fmap :: (a1 -> b) -> MyFunctor a a1 -> MyFunctor a b
          (bound at C:\Haskell\func.hs:3:3)
Run Code Online (Sandbox Code Playgroud)

ama*_*loy 5

然后我希望在操作之前将左值替换为正确的值.

对于Functor来说,这是非法的事情:GHC正确地告诉你这些类型无法解决.特别是,MyFunctor值的左侧部分可能与右侧部分的类型不同,如MyFunctor 5 "hello".并且由于fmap必须仅对您的类型的最后一个类型参数进行操作,因此必须单独留下第一部分.让我们看一个更具体的例子.

回想一下,类型fmap

fmap :: Functor f => (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

假设你有一个你想要的对象fmap:

obj :: MyFunctor Int String
Run Code Online (Sandbox Code Playgroud)

什么,那么,必须要f打电话的类型fmap f obj?为了统一所涉及的类型,我们必须拥有

f :: (String -> a)
Run Code Online (Sandbox Code Playgroud)

fmap f obj :: MyFunctor Int a
Run Code Online (Sandbox Code Playgroud)

所以你可以看到用第二个字符串字段的前一个值"替换"第一个Int字段是不可能的:在那个地方唯一允许的就是之前的那个,一个Int!

现在,您可以想象如果更改了MyFunctor定义,以便两个字段具有相同的类型,则可以使类型工作正常:

data MyFunctor a = MyFunctor a a

-- Also illegal
instance Functor MyFunctor where
  fmap f (MyFunctor a b) = MyFunctor b (f b)
Run Code Online (Sandbox Code Playgroud)

但是你也不能这样做,因为b并且f b可能是不同的类型,并且调用者可以选择f使用什么,所以你的实现fmap可能不会假设它们是相同的.

实际上,对于任何给定的数据类型,Functor最多只有一个合法的定义:如果你找到一个合法的定义,你可以确定任何其他定义都是非法的:要么类型不匹配,要么打破两个Functor法则之一:

fmap id == id
fmap f . fmap g == fmap (f . g)
Run Code Online (Sandbox Code Playgroud)

你怎么可能在这些类型排除的同时打破其中一条法则?通过操作某些不属于被fmap编辑结构的结构.例如,经常有人写一个(坏!)Functor,如下所示:

data Counter a = Counter Int a

instance Functor Counter where
  fmap f (Counter n x) = Counter (n + 1) (f x)
Run Code Online (Sandbox Code Playgroud)

但是这打破了两个Functor定律,因为它允许你计算fmap被调用的次数,这应该是一个未被暴露的细节.

fmap id (Counter 0 0) == Counter 1 0
(fmap tail . fmap tail) /= fmap (tail . tail)
Run Code Online (Sandbox Code Playgroud)