在Haskell中,lift和扩展的二元概念是绝对常用的,可能最突出的是作为共范畴extend
和单范畴bind
的代表。 (令人困惑的是,extend
是一个lift而不是一个extension。)一个共范畴w
的extend
可以让我们沿着extract :: w b -> b
将函数w a -> b
提升到w a -> w b
的映射中。在ASCII艺术中,给定图表
w b
|
V
w a ---> b
其中垂直箭头为提取,extend
给我们一个对角箭头(使图表可交):
-> w b
/ |
/ V
w a ---> b
对于大多数 Haskell 程序员来说更为熟悉的是单子 m
的双重概念:绑定 (>>=
)。给定一个函数 a -> m b
和 return :: a -> m a
,我们可以沿着 return
"扩展" 函数以获得一个函数 m a -> m b
。在 ASCII 艺术中:
a ---> m b
|
V
m a
给我们
a ---> m b
| __A
V /
m a
(那个箭头是箭头!)
因此,extend
本来可以被称为lift
,而bind
本来可以被称为extend
。至于Haskell的lift
s,我不知道它们为什么会被叫做那样!
编辑:实际上,我想再次指出,Haskell的lift
s实际上是扩展(extensions)。如果f
是应用函子(applicative),并且我们有一个函数a -> b -> c
,我们可以将这个函数与pure :: c -> f c
组合,得到一个函数a -> b -> f c
。去除柯里化,这就相当于一个函数(a,b) -> f c
。现在我们还可以使用pure
击中(a,b)
,获得一个函数(a,b) -> f (a,b)
。现在,通过对fst
和snd
进行fmap
,我们得到两个函数f (a,b) -> f a
和f (a,b) -> f b
,我们可以将它们合并成一个函数f (a,b) -> (f a,f b)
。通过之前的pure
组合,得到(a,b) -> (f a,f b)
。总结一下,我们有ASCII艺术图如下:
(a, b) ---> f c
|
V
(f a, f b)
现在,liftA2
给我们一个函数 (f a, f b) -> f c
,我不会画它,因为我已经厌倦了画糟糕的图表。但重点是,这个图表可交换,所以 liftA2
实际上给我们沿着垂直箭头的水平箭头的扩展。
fmap
中,在许多其他情况下也是如此。 “Liftings”的示例包括:
fmap :: (a -> b) -> F a -> F b
其中 F
是一个函子cmap :: (b -> a) -> F a -> F b
其中 F
是一个反变函子 bind :: (a -> M b) -> M a -> M b
其中 M
是一个单子ap :: F (a -> b) -> F a -> F b
其中 F
是一个应用函子point :: (_ -> a) -> _ -> F a
其中 F
是一个指向函子filtMap :: (a -> Maybe b) -> F a -> F b
其中 F
是一个可过滤函子extend :: (M a -> b) -> M a -> M b
其中 M
是一个余单子其他例子包括适用的反函子、可过滤的反函子和共指函子。
所有这些类型签名在某种程度上都很相似:它们将在a
和b
之间映射一种函数到另一种函数。
在这些不同的情况下,函数类型不仅仅是a -> b
,而是具有某种“扭曲”的类型:例如a -> M b
或F (a -> b)
或M a -> b
或F a -> F b
等。然而,每次定律都非常相似:扭曲的函数类型需要具有身份和组合法则,并且扭曲的组合需要是可结合的。
F(a -> b)
的函数。因此,我们需要定义一个特殊的“扭曲”恒等函数(pure id :: F(a -> a)
)和一个“扭曲”的组合操作,称之为apcomp
,其类型签名为F(a -> b) -> F(b -> c) -> F(a -> c)
。这个操作需要具有恒等律和结合律。 ap
操作需要具有恒等律和组合律(“扭曲恒等映射到扭曲恒等”,“扭曲组合映射到扭曲组合”)。a
、b
,... 等,态射为 F(a -> b)
)和 F-lifted 类别(对象为 F a
、F b
,... 等,态射为 F a -> F b
)。这两个类别之间的一个函子需要求出态射的 lifting,ap :: F(a -> b) -> F a -> F b
。对于 ap
的法则完全等价于该函子的标准法则。