请有人请给我一个例子
invmap :: (a -> b) -> (b -> a) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)
什么是Invariant有益?
大多数情况下,人们不会使用Invariant.你想要的原因是你正在处理一个变量出现在协变和逆变位置的类型.
newtype Endo a = Endo {appEndo :: a -> a}
newtype Foo a = Foo (Maybe a -> IO a)
data Bar a = Bar [a] (a -> Bool)
Run Code Online (Sandbox Code Playgroud)
这些都不是Functor或的实例Contravariant,但它们都可以是实例Invariant.
人们很少打扰的原因是,如果你需要对这种类型进行大量的映射,通常最好将其分解为协变和逆变部分.每个不变函子可以用以下表达式表示Profunctor:
newtype FooP x y = FooP (Maybe x -> IO y)
data BarP x y = Bar [y] (x -> Bool)
Run Code Online (Sandbox Code Playgroud)
现在
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)
Run Code Online (Sandbox Code Playgroud)
它通常更容易看到发生了什么事情,如果你解开的newtype,dimap在底层Profunctor,然后把它包起来再而不是乱搞invmap.
我们如何将Invariant仿函数转换为Profunctor?首先,让我们处理金额和产品.如果我们可以把f和g成profunctors fp和gp,那么我们一定能够把f :+: g和f :*: g成等价profunctor资金和产品.
作文怎么样?它有点棘手,但并不多.假设我们可以转向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
Run Code Online (Sandbox Code Playgroud)
现在假设你有一个函数类型; f a -> g a.这看起来像fp b a -> gp a b.
我认为应该涵盖大多数有趣的案例.