Haskell 自然地图推导算法
为Haskell 自然地图推导算法,haskell,covariance,functor,contravariance,denotational-semantics,Haskell,Covariance,Functor,Contravariance,Denotational Semantics,为fmap(我在另一篇文章中读到)提供了自然地图的构造性定义,该定义来自自由定理: 对于给定的f、g、h和k,例如f。g=h。k:$map f。fmap g=fmap h$map k,其中$map是给定构造函数的自然映射 我不完全理解算法。让我们一步一步来解决这个问题: 我们可以通过归纳您给出的任何具体的F选择来定义“自然地图”。 最终,任何此类ADT都由总和、乘积、(>)、1s、0s、a、调用其他 函子等 考虑: data Smth a = A a a a | B a (Maybe a) |
fmap
(我在另一篇文章中读到)提供了自然地图的构造性定义,该定义来自自由定理:
对于给定的f
、g
、h
和k
,例如f。g=h。k
:$map f。fmap g=fmap h$map k
,其中$map
是给定构造函数的自然映射
我不完全理解算法。让我们一步一步来解决这个问题:
我们可以通过归纳您给出的任何具体的F
选择来定义“自然地图”。
最终,任何此类ADT都由总和、乘积、(>)
、1
s、0
s、a
、调用其他
函子等
考虑:
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
应用于每个x
,将fmap xy
应用于每个tx
,其中T
是一个函子
,我们保持每个单元不变,并将每个Void
传递到上。这也适用于递归定义
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)
(详细解释见链接帖子下方。)
这一部分很清楚
当我们允许(>)
时,我们现在有了第一个混合方差的东西。(>)
的左侧类型参数处于逆变位置,右侧类型参数处于协变位置。因此,您需要在整个ADT中跟踪最终类型变量,并查看它是否出现在正位置和/或负位置
现在我们包括(>)
s。让我们试着让这一归纳继续下去:
我们以某种方式导出了ta
和sa
的自然地图。因此,我们要考虑以下几个方面:
data T2S a = T2S (T a -> S a)
instance ?Class? T2S where
?map? ?? (T2S tx2sx) = T2S $ \ ty -> ???
我相信这是我们开始选择的地方。我们有以下选择:
a
既不出现在T
中,也不出现在S
中<T2S
中的code>a
是幻影,因此,我们可以实现fmap
和contracmap
作为幻影
a
出现在sa
中,而不出现在ta
中。因此,这是ReaderT
行中使用sa
(实际上并不依赖a
)作为环境的一部分,它用函子
,映射
使用fmap
,?
,类
,用xy
替换:
let tx = phantom ty
sx = tx2sx tx
sy = fmap xy sx
in sy
a
出现在ta
中,不出现在sa
中。我看不到一种方法可以使这个东西成为协变函子,所以我们在这里实现了一个逆变
实例,它用逆变
替换?
,用yx
替换?
:
let tx = fmap yx ty
sx = tx2sx tx
sy = phantom sx
in sy
a
同时出现在ta
和sa
中。我们不能再使用幻影了,它很方便。有一个由Edward Kmett编写的模块。它为以下类提供了一个映射:
class Invariant f where
invmap :: (a -> b) -> (b -> a) -> f a -> f b
-- and some generic stuff...
然而,我看不出有什么方法可以把它变成fmap的自由定理——这个类型需要一个额外的函数参数,我们不能把它当作id
或其他什么。无论如何,我们将invmap
替换为?map?
,xy yx
替换为?
,以下替换为?
:
let tx = fmap yx ty
sx = tx2sx tx
sy = fmap xy sx
in sy
那么,我对这种算法的理解正确吗?如果是这样,我们如何正确处理不变量情况?我认为您的算法太复杂了,因为您正在尝试编写一个算法。相反,编写两个算法会使事情变得更简单。一种算法将构建自然fmap,另一种算法将构建自然对照图。但这两种算法在以下意义上都需要是不确定的:存在无法成功的类型,因此不返回实现;有些类型的人有多种成功的方法,但它们都是等价的 首先,让我们仔细定义参数化类型的含义。以下是我们可以拥有的不同类型的参数化类型:
F ::= F + F'
| F * F'
| F -> F'
| F . F'
| Id
| Const X
在Const X
中,X
覆盖所有具体的非参数化类型,如Int
和Bool
等。这是它们的解释,也就是说,一旦给定一个参数,它们就同构于具体类型:
[[F + F']] a = Either ([[F]] a) ([[F']] a)
[[F * F']] a = ([[F]] a, [[F']] a)
[[F -> F']] a = [[F]] a -> [[F']] a
[[F . F']] a = [[F]] ([[F']] a)
[[Id]] a = a
[[Const X]] a = X
现在我们可以给出两种算法。您自己已经编写的第一部分:
fmap @(F + F') f (Left x) = Left (fmap @F f x)
fmap @(F + F') f (Right x) = Right (fmap @F' f x)
fmap @(F * F') f (x, y) = (fmap @F f x, fmap @F f y)
fmap @(Id) f x = f x
fmap @(Const X) f x = x
这些条款与你第一次提出的条款一致。然后,在[Graph a]
示例中,您给出了一个与此对应的子句:
fmap @(F . F') f x = fmap @F (fmap @F' f) x
这很好,但这也是我们第一次得到一些不确定性。使其成为函子的一种方法是嵌套fmap
s;但另一种方法是嵌套contracmap
s
fmap @(F . F') f x = contramap @F (contramap @F' f) x
如果这两个子句都可以,那么F
或F'
中都没有Id
s,因此这两个实例都将返回x
不变
现在唯一剩下的就是你要问的那个箭盒。但事实证明,这种形式主义非常简单,只有一种选择:
fmap @(F -> F') f x = fmap @F' f . x . contramap @F f
这就是定义自然fmap
的整个算法的全部细节。。。除了一个细节,这是自然对映
的算法。但是如果你遵循以上所有的方法,你可以自己复制这个算法。我鼓励你试一试,然后对照下面我的答案核对一下
contramap @(F + F') f (Left x) = Left (contramap @F f x)
contramap @(F + F') f (Right x) = Right (contramap @F' f x)
contramap @(F * F') f (x, y) = (contramap @F f x, contramap @F' f y)
contramap @(F -> F') f x = contramap @F' f . x . fmap @F f
contramap @(F . F') f x = contramap @F (fmap @F' f) x
-- OR
contramap @(F . F') f x = fmap @F (contramap @F' f) x
-- contramap @(Id) fails
contramap @(Const X) f x = x
我个人感兴趣的一件事是:contra
contramap @(F + F') f (Left x) = Left (contramap @F f x)
contramap @(F + F') f (Right x) = Right (contramap @F' f x)
contramap @(F * F') f (x, y) = (contramap @F f x, contramap @F' f y)
contramap @(F -> F') f x = contramap @F' f . x . fmap @F f
contramap @(F . F') f x = contramap @F (fmap @F' f) x
-- OR
contramap @(F . F') f x = fmap @F (contramap @F' f) x
-- contramap @(Id) fails
contramap @(Const X) f x = x