【问题标题】:An example of a type with kind * -> * which cannot be an instance of Functorkind * -> * 类型的示例,它不能是 Functor 的实例
【发布时间】:2013-04-20 08:44:11
【问题描述】:

我是一个 Haskell 新手,如果答案很明显,我深表歉意,但我正在阅读 Typeclassopedia 以更好地理解类别。在做 Functors 部分的练习时,我遇到了这个问题:

举一个 * -> * 类型的例子,它不能作为一个实例 函子(不使用未定义的)。

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

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

谢谢!

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

【问题讨论】:

  • (-> int) 呢?
  • @RamonSnir ((->) Int) 实际上很好,你需要像data K a = K (a -> Int) 这样的东西。
  • @MikhailGlushenkov,这几乎肯定是 Ramon 的意思,就像 (+ 1) = \a -> a + 1
  • @MikhailGlushenkov 正如@dbaupp 所说,(-> int) ((->) int)

标签: haskell functional-programming


【解决方案1】:

一个简单的例子是

data K a = K (a -> Int)

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

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 -&gt; f b),但是您无法组合a -&gt; b 和@ 987654332@ 获得b -&gt; 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

【讨论】:

  • 谢谢,所以如果我理解正确,问题是对于 K 的 fmap 定义,我们有 (a -&gt; b) -&gt; K (a -&gt; Int) -&gt; K (a -&gt; b),它将通过尝试组合一个 a -&gt; Int 类型的函数和一个函数来定义a -&gt; b 类型的,这不起作用,因为 a 类型必须固定为 Int
  • @JS 你有一个a -&gt; b类型的函数和一个a -&gt; Int类型的函数,你必须产生一个b -&gt; Int类型的函数。但是您无法组合输入以获得所需的输出。
  • 无需定义Contravariant类,使用the one from Hackage
【解决方案2】:

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

鉴于(-&gt; Int),您会期望fmap 看起来像这样:

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

或:

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

很容易证明,从(a -&gt; Int)(a -&gt; b)b 三个参数中,没有可能到达Int(没有undefined),因此从(a -&gt; Int)(a -&gt; b)无法联系到(b -&gt; Int)。结论:Functor 不存在 (-&gt; Int) 的实例。

【讨论】:

  • 证明很简单:我们有两个定理,都需要证明a。我们只有b 的证明。无法推导出新的证明(因为没有任何定理适用)=> 无法推导出 Int 的证明。
【解决方案3】:

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

我在理解 (a -&gt; Int) 的协变时遇到了困难,并想出了这个反例,显示 data K a = K (a -&gt; 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)))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-15
    • 2012-12-13
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多