双类型Functor定义被拒绝

时间:2017-11-24 16:28:55

标签: haskell types compiler-errors functor

为什么这个仿函数定义被拒绝了?

data Second a b = Ok a b | Ko a b deriving (Show)

instance Functor (Second x) where
  fmap f (Ok a b ) = Ok (f a) b
  fmap f (Ko a b ) = Ko a (f b) 

get a lot of errors

GHCi, version 8.0.1

main.hs:4:22: error:
    • Couldn't match type ‘b’ with ‘x’
      ‘b’ is a rigid type variable bound by
        the type signature for:
          fmap :: forall a b. (a -> b) -> Second x a -> Second x b
        at main.hs:4:3
      ‘x’ is a rigid type variable bound by
        the instance declaration at main.hs:3:10
      Expected type: Second x b
        Actual type: Second b a
    • In the expression: Ok (f a) b
      In an equation for ‘fmap’: fmap f (Ok a b) = Ok (f a) b
      In the instance declaration for ‘Functor (Second x)’
    • Relevant bindings include
        a :: x (bound at main.hs:4:14)
        f :: a -> b (bound at main.hs:4:8)
        fmap :: (a -> b) -> Second x a -> Second x b (bound at main.hs:4:3)
main.hs:4:28: error:
    • Couldn't match expected type ‘a’ with actual type ‘x’
      ‘x’ is a rigid type variable bound by
        the instance declaration at main.hs:3:10
      ‘a’ is a rigid type variable bound by
        the type signature for:
          fmap :: forall a b. (a -> b) -> Second x a -> Second x b
        at main.hs:4:3
    • In the first argument of ‘f’, namely ‘a’
      In the first argument of ‘Ok’, namely ‘(f a)’
      In the expression: Ok (f a) b
    • Relevant bindings include
        b :: a (bound at main.hs:4:16)
        a :: x (bound at main.hs:4:14)
        f :: a -> b (bound at main.hs:4:8)
        fmap :: (a -> b) -> Second x a -> Second x b (bound at main.hs:4:3)

这一切意味着什么?请帮忙。

1 个答案:

答案 0 :(得分:6)

如果您解压缩Functor的定义,您会看到Second的第一个参数保持不变,而其第二个参数可用于转换。

class Functor f where
  fmap :: (s -> t) -> f s -> f t

(我已将类型变量ab分别重命名为st,因为您还有值变量ab。)

class在您的具体instance

中为我们提供了帮助
instance Functor (Second x) where
  -- fmap :: (s -> t) -> (Second x) s -> (Second x) t
  --    i.e. (s -> t) -> Second x s   -> Second x t

现在,当您实施fmap时,您必须确保fmap用户可以选择任何xs和他们想要t。所以你不必对它们做任何假设:它们代表任意的,可能是不同的类型。这就是当他们谈论"刚性"时错误信息的含义。类型变量:您的代码不允许为它们选择特定类型,以便其用户可以。编译器抱怨你承诺一个非常多态的函数,但只提供一个较少的多态函数,只有在x=s=t时才会检查。

也就是说,当你写

  fmap f (Ok a b) = Ok (f a) b

你有

f :: s -> t
Ok a b :: Second x s
a :: x
b :: s

你要回来了

Ok (f a) b :: Second x t

需要

f a :: x   -- clearly not true, as f :: s -> t
b :: t     -- clearly not true, as b :: s

f a需要

a :: s     -- clearly not true, as a :: x

所以,是的,很多错误。

Functor实例允许您转换与类型 last 参数对应的位置的数据,即

中的b
data Second a b = Ok a b | Ko a b deriving (Show)

所以你的

  fmap f (Ko a b ) = Ko a (f b)

很好,但你的

  fmap f (Ok a b ) = Ok (f a) b

使用不同的参数,但不行。

  fmap f (Ok a b ) = Ok a (f b)

会奏效。将data声明更改为

也是如此
data Second a b = Ok b a | Ko a b deriving (Show)

并保留instance原样。

要么修复有效,要么不要同时做到"只是为了安全起见"!