“整洁的替代呈现”Applicative
基于以下两个等价物
pure a = fmap (const a) unit
unit = pure ()
ff <*> fa = fmap (\(f,a) -> f a) $ ff ** fa
fa ** fb = pure (,) <*> fa <*> fb
获得这种“简洁的替代表示”Applicative
的技巧与技巧相同zipWith
- 将接口中的显式类型和构造函数替换为可以传递类型或构造函数以恢复原始接口的内容。
unit :: f ()
替换为pure
我们可以将类型()
和构造函数替换() :: ()
为恢复unit
。
pure :: a -> f a
pure () :: f ()
(a,b)
同样(尽管不是那么简单)将类型和构造函数替换(,) :: a -> b -> (a,b)
为liftA2
恢复**
。
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 (,) :: f a -> f b -> f (a,b)
Applicative
<*>
然后通过将函数应用程序提升($) :: (a -> b) -> a -> b
到函子中来获得不错的运算符。
(<*>) :: f (a -> b) -> f a -> f b
(<*>) = liftA2 ($)
为了找到一个“整洁的替代演示文稿”,PtS
我们需要找到
- 我们可以将类型替换
Void
为恢复的东西unit
- 我们可以替换类型
Either a b
和构造函数Left :: a -> Either a b
并Right :: b -> Either a b
恢复的东西**
(如果您注意到我们已经有了构造函数Left
并且Right
可以传递给您,那么您可能会在不遵循我使用的步骤的情况下找出我们可以替换**
的内容;直到我解决它之后我才注意到这一点)
单元
这立即为我们提供unit
了求和的替代方法:
empty :: f a
empty = fmap absurd unit
unit :: f Void
unit = empty
操作员
我们想找到(**)
. 除了总和之外,还有一种替代方法Either
,可以将它们写成乘积的函数。它在不存在总和的面向对象编程语言中显示为访问者模式。
data Either a b = Left a | Right b
{-# LANGUAGE RankNTypes #-}
type Sum a b = forall c. (a -> c) -> (b -> c) -> c
如果你改变either
's 参数的顺序并部分应用它们,你会得到什么。
either :: (a -> c) -> (b -> c) -> Either a b -> c
toSum :: Either a b -> Sum a b
toSum e = \forA forB -> either forA forB e
toEither :: Sum a b -> Either a b
toEither s = s Left Right
我们可以看到Either a b ≅ Sum a b
。这允许我们重写类型(**)
(**) :: f a -> f b -> f (Either a b)
(**) :: f a -> f b -> f (Sum a b)
(**) :: f a -> f b -> f ((a -> c) -> (b -> c) -> c)
现在很清楚是做什么的**
。它将一些东西延迟fmap
到它的两个参数上,并结合这两个映射的结果。如果我们引入一个 new 运算符,<||> :: f c -> f c -> f c
它只是假设fmap
ing 已经完成,那么我们可以看到
fmap (\f -> f forA forB) (fa ** fb) = fmap forA fa <||> fmap forB fb
或返回Either
:
fa ** fb = fmap Left fa <||> fmap Right fb
fa1 <||> fa2 = fmap (either id id) $ fa1 ** fa2
所以我们可以PtS
用下面的类来表达一切能表达的东西,能实现PtS
的东西都可以实现下面的类:
class Functor f => AlmostAlternative f where
empty :: f a
(<||>) :: f a -> f a -> f a
这几乎可以肯定与Alternative
类相同,只是我们不要求Functor
be Applicative
。
结论
它只是一个Functor
适用Monoid
于所有类型的。它相当于以下内容:
class (Functor f, forall a. Monoid (f a)) => MonoidalFunctor f
forall a. Monoid (f a)
约束是伪代码;我不知道如何在 Haskell 中表达这样的约束。