有人可以给我举个例子吗
invmap :: (a -> b) -> (b -> a) -> f a -> f b
Invariant 有什么好处?
最佳答案
大多数情况下,人们不使用Invariant
。您想要这样做的原因是,如果您使用的类型中变量同时出现在协变和逆变位置。
newtype Endo a = Endo {appEndo :: a -> a}
newtype Foo a = Foo (Maybe a -> IO a)
data Bar a = Bar [a] (a -> Bool)
这些都不是仿函数
或逆变
的实例,但它们都可以是不变
的实例。
人们很少打扰的原因是,如果您需要对这种类型进行大量映射,通常最好将其分解为协变和逆变部分。每个不变仿函数都可以用 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)
如果您将 newtype
、dimap
展开到底层 Profunctor
上,然后再次将其包裹起来,通常会更容易看到发生了什么而不是乱搞 invmap
。
我们如何将Invariant
仿函数转换为Profunctor
?首先,让我们处理总和和乘积。如果我们可以将f
和g
转为仿函数fp
和gp
,那么我们一定可以将f :+: g
和 f :*: g
转换成等价的仿函数和和积。
作曲怎么样?这有点棘手,但不多。假设我们可以将 f
和 g
转换为仿函数 fp
和 gp
。现在定义
-- 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/