Contramap的目的是什么?

时间:2018-02-02 16:52:33

标签: haskell

我阅读了很多关于Contramap的文章,发现https://hackage.haskell.org/package/contravariant-1.4.1/docs/Data-Functor-Contravariant.html#g:3是最好的。

无论如何我发现,如何使用例如:

*Lib Data.Functor.Contravariant> a = Predicate (\x -> x > 20)
*Lib Data.Functor.Contravariant> :t contramap
contramap :: Contravariant f => (a -> b) -> f b -> f a
*Lib Data.Functor.Contravariant> :t contramap (\x -> x * 2)
contramap (\x -> x * 2) :: (Num b, Contravariant f) => f b -> f b
*Lib Data.Functor.Contravariant> :t contramap (\x -> x * 2) a
contramap (\x -> x * 2) a :: (Ord b, Num b) => Predicate b
*Lib Data.Functor.Contravariant> x = contramap (\x -> x * 2) a
*Lib Data.Functor.Contravariant> getPredicate x 45
True 

但无法弄清楚,哪里有用。

在我上面发布的网站上,它说:

  

然而在Haskell中,人们可以将Functor视为包含或者   产生价值,逆变函子是一个可以的函子   被认为是消费价值观。

看看仿函数的定义:

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

它使用类型a的值,并生成类型b的值。在contramap上消耗该值。

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

它使用哪种变量类型ab

https://www.fpcomplete.com/blog/2016/11/covariance-contravariance关于积极和消极情况的问题。在网站上它说:

  • 正位置:类型变量是 函数的结果/输出/范围/ codomain
  • 负位置:类型变量是参数/输入/域 功能

查看Contramap类型定义:

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

作者指向哪个类型变量?

2 个答案:

答案 0 :(得分:6)

玩家f被认为包含'或者'生产' f a就像a值的容器一样意义上的值。 fmap允许您转换f所持有的值 示例:

  • [a]'包含'类型为a
  • 的非负数
  • IO a可能会执行一些IO和'返回'或者'生产'类型为a
  • 的值
  • (->) r a'包含' a
  • 的每个值的类型r的值

现在,Contravariant f是可以吸引的contramap。或者'消费'值。 f a允许您在消耗它们之前转换newtype Op r a = Op { runOp :: a -> r }之前的内容 这方面的主要例子通常是使用类似的东西 Predicate
(注意您使用的Op Bool似乎只是Op (IO ())
现在我们有了一些可以消费的东西。 a。类型的值(类比可能对o = Op (\x -> putStrLn x) :: Op (IO ()) String更有意义)
继续这个消费的例子'如果我们想要使用o,但是对于Show a => a类型的值,请考虑contramap。以及contramap show o :: Show a => Op (IO ()) a的用途! runOp (contramap show o)
(请注意,在这个简单的情况下,print只是Contravariant

编辑:
关于Contravariant c, Contravariant d, Functor f的另一个有趣的事情是关于编写它 给定newtype Compose f g a = Compose { runCompose :: f (g a) }
Compose f c
我们有:

  • Contravariant也是contramap f (Compose fca) = Compose $ fmap (contramap f) fca
    Compose c f
  • Contravariant也是contramap f (Compose cfa) = Compose $ contramap (fmap f) cfa
    Compose c d
  • Functor实际上是fmap f (Compose cda) = Compose $ contramap (contramap f) cda
    {{1}}

答案 1 :(得分:5)

  

看看仿函数的定义:

a
     

它使用b类型的值,并生成fmap类型的值。

这不准确,是您混淆的根源。

特别是,a -> b类型中有三个不同的对象:函数f a,"函数"价值f b," functorial"值a,以及b是否被消耗或生成的内容因这些对象而异,a -> b是否被消费或生成。

  • a类型的函数使用b并生成f。 (这是您所说的句子,但是添加了一个新的附加内容,指定适用于哪个对象。)
  • 如果Functorf a,那么类型a"的值会产生&#34}。 f秒。
  • 如果Functorf b,那么类型b"的值会产生&#34}。 f秒。

我们可以将这些观察扩展到更大的函数类型。

  • 如果Functorf a -> f b,那么f a类型的函数会消耗f b s并生成a s;并且因为仿函数是正的,这反过来意味着函数消耗b并生成f s。
  • 如果Functor(a -> b) -> f a -> f b,那么a -> b类型的函数会消耗a - 也就是说,在实现中,我们将生成a -> b提供给b函数并使用它返回的f a -> f b s!这样做会产生a,消耗b并产生fmap s。

N.B。 "消费"的作用和"生产"在上面的描述中,即使在称为fmap的单个对象中,也会发生很多变化;所以说" a消耗b s并生成Functor s"并不是在讲述整个故事。

以下陈述也以类似的方式不精确:

  

在Haskell中,人们可以将fmap视为包含或生成值,而逆变函子是一种可以被认为是消费价值的算子。

您已将此解释为表示contramap作为(函数)对象包含值,而f a生成值。但这不是预期的;相反,该陈述的目的是关于(逆变)函数值本身,而不是应用于它们的(反)映射。这个更准确的说法是:

  

在Haskell中,人们可以将f类型的值(Functora)视为包含或生成f值,而不是变换函数f a会产生a类型的值,可以将其视为消耗{{1}}值。

关于积极和消极职位的问题,您可以享受some of my previous discussion of this topic here on SO