我阅读了很多关于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
它使用哪种变量类型a
或b
?
https://www.fpcomplete.com/blog/2016/11/covariance-contravariance关于积极和消极情况的问题。在网站上它说:
查看Contramap类型定义:
contramap :: (a -> b) -> f b -> f a
作者指向哪个类型变量?
答案 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 :(得分:5)
看看仿函数的定义:
a
它使用
b
类型的值,并生成fmap
类型的值。
这不准确,是您混淆的根源。
特别是,a -> b
类型中有三个不同的对象:函数f a
,"函数"价值f b
," functorial"值a
,以及b
是否被消耗或生成的内容因这些对象而异,a -> b
是否被消费或生成。
a
类型的函数使用b
并生成f
。 (这是您所说的句子,但是添加了一个新的附加内容,指定适用于哪个对象。)Functor
是f a
,那么类型a
"的值会产生&#34}。 f
秒。Functor
是f b
,那么类型b
"的值会产生&#34}。 f
秒。我们可以将这些观察扩展到更大的函数类型。
Functor
是f 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
类型的值(Functor
是a
)视为包含或生成f
值,而不是变换函数f a
会产生a
类型的值,可以将其视为消耗{{1}}值。
关于积极和消极职位的问题,您可以享受some of my previous discussion of this topic here on SO。