【问题标题】:What is a covariant functor?什么是协变函子?
【发布时间】:2019-03-19 07:17:42
【问题描述】:

我想明白,为什么例如Maybe 类型是协变函子?

协变是什么意思?

请举例说明。

【问题讨论】:

  • 协变函子被描述为here (FPComplete.com)。有一个使用Maybe的例子。
  • 这道题主要是关于数学的。 Haskell 中的所有Functors 都是协变的。逆变函子将是具有函数contramap :: (a -> b) -> f b -> f a 的类型f - 请注意,与fmap 相比,生成的函数具有“反向”箭头。 This does exist in Haskell,但不像(协变)Functor 那样普遍。
  • This is a question about mathematics primarily 这让 Haskell 害怕学习。我不是数学家,但我想了解它。问题是,如何向非数学家解释?
  • @zero_coding 我不会说这真的是关于数学的。这个概念确实来自数学(特别是范畴论),但你真的不需要知道数学来理解这个概念。我发现这对于 Haskell 来说也是如此。
  • 澄清一下——我完全同意@bradrn 的观点,即您无需了解任何深奥的数学知识即可使用 Haskell。这实际上就是我想说的,在某种程度上——“协变意味着什么”这个问题是一个关于数学的问题,你不需要知道或理解这个术语就可以在 Haskell 中使用 Functor。我不知道如何在不谈论数学而不是 Haskell 的情况下回答这个问题——但显然有 2 个人做到了并且写出了很好的答案。抱歉,如果这听起来像是我试图让你失望,那与我的意图完全相反。

标签: haskell


【解决方案1】:

协变函子只是普通的Functor类:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

例如,Maybe(如您所述):

instance Functor Maybe where
    fmap _ Nothing = Nothing
    fmap f (Just a) = Just (f a)

但是,还有另一种类型的函子:逆变函子。这些定义如下:

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

请注意,与fmap相比,contramap颠倒了ba的顺序:

fmap      ::       Functor f => (a -> b) -> f a -> f b
contramap :: Contravariant f => (a -> b) -> f b -> f a
                                         --   ^      ^
                                         --   look!

现在,这个疯狂的Contravariant 类是否有任何实例?嗯,是。例如,这是Predicate的定义:

newtype Predicate x = Predicate { decide :: x -> Bool }

换句话说,Predicate x 是一个计算x 条件的函数。我们可以将contramap 专门化为Predicates:

contramap :: (a -> b) -> Predicate b -> Predicate a

相当于:

contramap :: (a -> b) -> (b -> Bool) -> (a -> Bool)

基本上,给定bs 上的Predicate,以及从as bs 的映射,您可以通过contramap 在@ 上获得Predicate 987654346@s。 (我将把实现留作练习。)这是一个示例(未经测试):

hasMultChars :: Predicate String
hasMultChars = Predicate $ \x -> length x > 1

showInt :: Int -> String
showInt = show

intHasMultChars :: Predicate Int
intHasMultChars = contramap showInt hasMultChars

事实证明,与普通协变函子相比,逆变函子要少得多,所以用处也少得多。所以在实践中,我们省略了“协变”,因为在大多数情况下它不会添加任何东西。

【讨论】:

  • 谢谢@DamianLattenero! (尽管请注意,Stack Overflow cmets 系统不鼓励这样的 cmets;您能否改为投票?)
  • 对不起@DamianLattenero,我一定错过了。
【解决方案2】:

协变函子是“内”和“外”箭头指向同一方向的函子。

class Functor f where
  fmap :: (a -> b) -> (f a -> f b)

逆变函子是“内部”和“外部”箭头指向相反方向的函子。

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

...或者,使用正确的 Haskell 语法,

  contramap :: (a -> b) -> (f b -> f a)

这通常表明参数类型作为数据类型中的函数参数出现在某处,例如

data DepInt x = DepInt (x -> Int)

instance Contravariant DepInt where
  contramap f (DepInt g) = DepInt $ g . f

相反,如果参数仅按原样出现或出现在函数箭头的右侧,则它是协变函子。大多数函子都是这种情况,这就是为什么这个类被简单地称为Functor

【讨论】:

  • 一般来说,co-'something' 是指箭头颠倒的'something'。
  • @bradrn 除非“co”后跟“variant”!
  • @bradrn 这并不完全正确,例如在图论中co- 作为前缀通常表示“该图的补图”,因此“cobipartite”将是二分图的补图类.补图是具有补边关系的图,而不是任何箭头反转的图。
  • @bradrn 我不确定我是否遵循。 “co-”在这里被用在不同的上下文中。另外,a "cofunctor" is the same as a functor(维基词典声称“协函子”是“逆变函子”的同义词,但到目前为止,这是我看到该声明的唯一地方,并且没有引用。也许需要更新?)。
  • @David 是的,协函子与函子相同。如果你反转fmap :: Functor f =&gt; (a -&gt; b) -&gt; f a -&gt; f b 上的箭头,你会得到co_fmap :: CoFunctor f =&gt; (a &lt;- b) -&gt; (f a &lt;- f b),它与co_fmap :: CoFunctor f =&gt; (b -&gt; a) -&gt; (f b -&gt; f a) 相同——与fmap 相同。 (考虑到我的评论,我什至不确定我为什么这样做!问题或这个答案中都没有提到共同的东西,所以唯一合理的解释是我当时产生了幻觉......)
猜你喜欢
  • 2016-10-28
  • 1970-01-01
  • 1970-01-01
  • 2015-09-08
  • 2017-08-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多