有人可以给我提供一个例子

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


对于不变式有什么好处?

最佳答案

通常,人们不使用Invariant。您想要的原因是,如果您正在使用一种变量同时出现在协变和反变位置的类型。

newtype Endo a = Endo {appEndo :: a -> a}
newtype Foo a = Foo (Maybe a -> IO a)
data Bar a = Bar [a] (a -> Bool)


这些都不是FunctorContravariant的实例,但是它们都可以是Invariant的实例。

人们很少打扰的原因是,如果您需要对此类类型进行大量映射,那么通常最好将其分解为协变和反变量部分。每个不变函子可以用Profunctor表示:

newtype FooP x y = FooP (Maybe x -> IO y)
data BarP x y = Bar [y] (x -> Bool)


现在

Endo a ~= (->) a a
Foo a ~= FooP a a
Bar a ~= BarP a a
-- So we'd likely write newtype Bar a = Bar (BarP a a)


通常,如果将newtypedimap展开为基础的Profunctor,然后重新包装起来,而不是弄乱invmap,通常会更容易看到发生了什么。



如何将Invariant函子转换为Profunctor?首先,让我们处理总和与乘积。如果我们可以将fg转换为fpgp的修饰语,那么我们肯定可以将f :+: gf :*: g转换为等效的修饰语总和和乘积。

成分呢?有点棘手,但不多。假设我们可以将fg变成发音器fpgp。现在定义

-- Compose f g a ~= ComposeP fp gp a a
newtype ComposeP p q a b = ComposeP (p (q b a) (q a b))
instance (Profunctor p, Profunctor q) => Profunctor (ComposeP p q) where
  dimap f g (ComposeP p) = ComposeP $ dimap (dimap g f) (dimap f g) p


现在假设您有一个函数类型; f a -> g a。看起来像fp b a -> gp a b

我认为这应该涵盖大多数有趣的情况。

关于haskell - 如何使用Data.Functor.Invariant?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48708805/

10-08 23:31