22

我是一个 Haskell 新手,如果答案很明显,我很抱歉,但我正在通过 Typeclassopedia 努力更好地理解类别。在为 Functors 部分做练习时,我遇到了这个问题:

举一个 * -> * 类型的例子,它不能成为 Functor 的实例(不使用 undefined)。

我的第一个想法是定义某种无限递归的 fmap 定义,但这undefined与定义中使用的 if 本质上不一样吗?

如果有人可以解释答案,将不胜感激。

谢谢!

原始练习的来源,第 3 部分:http ://www.haskell.org/haskellwiki/Typeclassopedia#Introduction

4

3 回答 3

23

一个简单的例子是

data K a = K (a -> Int)

ghci 告诉我们的是,我们尝试自动Functor为 派生一个实例K

Prelude> :set -XDeriveFunctor
Prelude> data K a = K (a -> Int)
Prelude> :k K
K :: * -> *
Prelude> data K a = K (a -> Int) deriving Functor

<interactive>:14:34:
    Can't make a derived instance of `Functor K':
      Constructor `K' must not use the type variable in a function argument
    In the data type declaration for `K'

问题是标准Functor类实际上表示协变函子(fmap将其参数提升到f a -> f b),但是您无法组合a -> ba -> Int获得类型的函数b -> Int(请参阅 Ramon 的回答)。但是,可以为逆变函子定义类型类:

class Contravariant f where
    contramap :: (a -> b) -> f b -> f a 

并创建K一个实例:

instance Contravariant K where
    contramap f (K g) = K (g . f)

有关 Haskell 中协变/逆变的更多信息,请参见此处

编辑:这里也是Chris Smith 在 Reddit上对这个主题的一个很好的评论。

于 2013-04-20T09:01:07.390 回答
6

要扩展我的(简短)评论和米哈伊尔的回答:

鉴于(-> Int),您希望fmap看起来像这样:

(a -> Int) -> (a -> b) -> (b -> Int)

或者:

(a -> Int) -> (a -> b) -> b -> Int

很容易证明,从三个参数(a -> Int)(a -> b)b没有可能到达Int(没有undefined),因此从(a -> Int)(a -> b)没有办法到达(b -> Int)。结论:不Functor存在(-> Int).

于 2013-04-20T09:07:30.873 回答
0

我也遇到了这个问题,我发现 Ramon 和 Mikhail 的回答内容丰富——谢谢!我把它放在答案而不是评论中,因为 500 个字符太短,而且对于代码格式化。

我很难理解什么是协变的,(a -> Int)并想出了这个反例,显示data K a = K (a -> Int) 可以作为 Functor 的一个实例(反驳 Ramon 的证明)

data K a = K (a -> Int)
instance Functor K where
  fmap g (K f) = K (const 0)

如果它编译,它一定是正确的,对吧?;-) 我花了一些时间尝试其他排列。翻转函数只是让它更容易:

-- "o" for "output"
-- The fmapped (1st) type is a function output so we're OK.
data K0 o = K0 (Int -> o)
instance Functor K0 where
  fmap :: (oa -> ob) -> (K0 oa) -> (K0 ob)
  fmap g (K0 f) = K0 (g . f)

将 Int 转换为类型变量将其简化为第 3.2 节练习 1 第 2 部分:

-- The fmapped (2nd) type argument is an output
data K1 a b = K1 (a -> b)
instance Functor (K1 a) where
  fmap :: (b1 -> b2) -> K1 a b1 -> K1 a b2
  fmap g (K1 f) = K1 (g . f)

强制 fmapped 类型成为函数的参数是关键......就像米哈伊尔的回答所说,但现在我明白了;-)

-- The fmapped (2nd) type argument is an input
data K2 a b = K2 (b -> a) 
instance Functor (K2 o) where
  fmap :: (ia -> ib) -> (K2 o ia) -> (K2 o ib)
  -- Can't get our hands on a value of type o
  fmap g (K2 f) = K2 (const (undefined :: o))
  -- Nor one of type ia
  fmap g (K2 f) = K2 (const (f (undefined :: ia)))
于 2018-10-20T01:59:22.637 回答