类型* - >类型的示例*不能是Functor的一个实例

时间:2013-04-20 08:44:11

标签: haskell functional-programming

我非常喜欢Haskell新手,如果答案很明显就道歉,但我正在通过Typeclassopedia努力更好地理解类别。在为Functors部分进行练习时,我遇到了这个问题:

  

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

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

如果有人能够解释答案,我们将不胜感激。

谢谢!

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

3 个答案:

答案 0 :(得分:22)

一个简单的例子是

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中协方差/逆变的更多信息,请参阅here

编辑:以下是来自Reddit的Chris Smith的a nice comment on this topic

答案 1 :(得分: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)个实例。

答案 2 :(得分: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类型作为函数的参数是关键...就像Mikhail的回答所说,但现在我明白了;-)

-- 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)))