Edward Kmett 的这篇 Reddit 帖子提供了自然地图的建设性定义,它来自自由定理(fmap我在另一篇Edward Kmett 的帖子中读到):
对于给定
f的 ,和g,这样: , 其中是给定构造函数的自然映射。hkf . g = h . k$map f . fmap g = fmap h . $map k$map
我不完全理解算法。让我们一步一步地接近它:
我们可以通过对您给出的任何特定具体选择的归纳来定义“自然地图” 。
F最终,任何这样的 ADT 都是由 sum、products、(->)'s、1s、0s、a's、其他仿函数的调用等组成的。
考虑:
data Smth a = A a a a | B a (Maybe a) | U | Z Void deriving ...
没有箭头。让我们看看fmap(我认为这是任何没有 s 的 ADT 的自然选择(->))在这里如何操作:
instance Functor Smth where
fmap xy (A x x1 x2) = A (xy x) (xy x1) (xy x2)
fmap xy (B x xPlus1) = B (xy x) (fmap xy xPlus1)
-- One can pattern-match 'xPlus1' as 'Just x1' and 'Nothing'.
-- From my point of view, 'fmap' suits better here. Reasons below.
fmap _xy U = U
fmap _xy (Z z) = absurd z
这看起来很自然。更正式地说,我们适用xy于 every x,适用fmap xy于 every T x,其中Ta Functor,我们保持每个单元不变,然后将 every 传递Void给absurd。这也适用于递归定义!
data Lst a = Unit | Prepend a (Lst a) deriving ...
instance Functor Lst where
fmap xy Unit = Unit
fmap xy (Prepend x lstX) = Prepend (xy x) (fmap xy lstX)
Graph a = Node a [Graph a]
instance Functor Graph where
fmap xy (Node x children) = Node (xy x) (fmap (fmap xy) children)
这部分很清楚。
当我们允许时,
(->)我们现在有了第一个混合方差的东西。的左侧类型参数(->)处于逆变位置,右侧处于协变位置。因此,您需要通过整个 ADT 跟踪最终类型变量,并查看它是否出现在正和/或负位置。
现在我们包括(->)s。让我们尝试继续进行这种归纳:
我们以某种方式推导出和的自然映射。因此,我们要考虑以下几点:T aS a
data T2S a = T2S (T a -> S a)
instance ?Class? T2S where
?map? ?? (T2S tx2sx) = T2S $ \ ty -> ???
我相信这是我们开始选择的起点。我们有以下选择:
- (幻影)
a既不在T也不在S。ainT2S是幻像,因此,我们可以同时实现fmap和contramapasconst phantom。 - (协变)
a出现在S a和不出现在 中T a。因此,这在ReaderTwithS a(实际上并不依赖于a)作为环境的行中,它?Class?用Functor、?map?withfmap、???、??with代替xy:let tx = phantom ty sx = tx2sx tx sy = fmap xy sx in sy - (逆变)
a出现在T a,不出S a。我看不到让这个东西成为协变函子的方法,所以我们Contravariant在这里实现一个实例,它?map?用contramap、??withyx、???with 代替:let tx = fmap yx ty sx = tx2sx tx sy = phantom sx in sy - (不变量)
a同时出现在T a和S a。我们不能再使用phantom,它派上用场了。Edward Kmett有一个模块Data.Functor.Invariant。它为以下类提供了一个地图:
然而,我看不出有什么方法可以把它变成我们可以加入 fmap 的自由定理class Invariant f where invmap :: (a -> b) -> (b -> a) -> f a -> f b -- and some generic stuff...id的东西——这种类型需要一个额外的函数参数,我们不能把它当作什么东西刷掉。无论如何,我们将invmap代替?map?,xy yx代替??, 并将以下内容代替???:let tx = fmap yx ty sx = tx2sx tx sy = fmap xy sx in sy
那么,我对这种算法的理解正确吗?如果是这样,我们如何正确处理Invariant情况?