我正在阅读关于 applicative 的内容,并尝试理解 haskellbook 上的内容。
在书中,作者提到:
所以,使用 Applicative,我们可以为结构体创建一个 Monoid 并对值应用函数!
Monoid 如何与 applicative 相关联?
我正在阅读关于 applicative 的内容,并尝试理解 haskellbook 上的内容。
在书中,作者提到:
所以,使用 Applicative,我们可以为结构体创建一个 Monoid 并对值应用函数!
Monoid 如何与 applicative 相关联?
注意:我还没有拥有这本书,而且如果我没记错的话,至少一个作者在SO上很活跃,并且应该能够回答这个问题。 也就是说,单子(或者更确切地说是半群)背后的思想是,在该单子1中,您有一种方法可以从两个对象创建另一个对象:
mappend :: Monoid m => m -> m -> m
那么Applicative
怎么成为一个单子呢?正如您引用的话所说,它在结构上是一个单子。也就是说,我们从一个f something
开始,继续使用f anotherthing
,最后得到了一个f resulthing
,你已经猜到了:
amappend :: f (a -> b) -> f a -> f b
在我们继续之前,短暂的,非常短暂的时间内,让我们忘记f
具有* -> *
类型。我们最终得到了什么?
amappend :: f -> f -> f
这就是所谓的“单调结构”部分。这也是 Haskell 中 Applicative
和 Functor
的区别,因为在 Functor
中我们没有该属性:
fmap :: (a -> b) -> f a -> f b
-- ^
-- no f here
这也是我们尝试仅使用fmap
或其他函数时会遇到麻烦的原因:经过一次fmap
后,除非我们能够“在那个新结构中应用我们的新函数”,否则我们将被困住。这就带我们来到了你问题的第二部分:
所以,使用Applicative,我们为值进行函数应用!
函数应用是($)
。如果我们看一下<*>
,我们可以立即看到它们很相似:
($) :: (a -> b) -> a -> b
(<*>) :: f (a -> b) -> f a -> f b
如果我们忘记在(<*>)
中的f
,那么我们最终只会得到($)
。 因此,在我们的结构上下文中,(<*>)
只是函数应用:
increase :: Int -> Int
increase x = x + 1
five :: Int
five = 5
increaseA :: Applicative f => f (Int -> Int)
increaseA = pure increase
fiveA :: Applicative f => f Int
fiveA = pure 5
normalIncrease = increase $ five
applicativeIncrease = increaseA <*> fiveA
我想作者所谓的“函数应用”就是这个意思。我们突然可以将那些隐藏在我们结构中的函数应用于其他值。由于单调性,我们仍然呆在这个结构中。
话虽如此,我个人从不称之为单调的,因为 <*>
不作用于相同类型的两个参数,并且一个适用程序缺少空元素。
1对于真正的半群/幺半群,该操作应该是结合的,但这并不重要
class Functor f => Cappl f where unit :: Category c => f (c a a); comp :: Category c => f (c y z) -> f (c x y) -> f (c x z)
。这等同于Applicative
,也等同于看似不那么强大的class Functor f => Fappl f where funit :: f (a -> a); fcomp :: f (y -> z) -> f (x -> y) -> f (x -> z)
。我不知道是否有任何有趣的东西。 - dfeuerProfunctor c
,你是如何从 Fappl
转换到 Cappl
的? - Benjamin HodgsonFappl
到Applicative
再到(显然的)Cappl
,最后到(琐碎的)Fappl
。如果你在这个过程中遇到了困难,请告诉我。我可以给你一个提示,但可能太大了。 - dfeuer看一下以下类:
class Functor f => Monoidal f where
unit :: f ()
(**) :: f a -> f b -> f (a, b)
Applicative
的问题的Monoidal
类之前,让我们先看一下它的法则,遵守这些法则可以给我们提供一个单子:
f a
(x
) 等同于 f ((), a)
(unit ** x
),这给了我们左单位元。 (** unit) :: f a -> f ((), a)
,fmap snd :: f ((), a) -> f a
。f a
(x
) 也等同于 f (a, ())
(x ** unit
),这给了我们右单位元。 (unit **) :: f a -> f (a, ())
,fmap fst :: f (a, ()) -> f a
。f ((a, b), c)
((x ** y) ** z
) 等同于 f (a, (b, c))
(x ** (y ** z)
),这给了我们结合律。 fmap assoc :: f ((a, b), c) -> f (a, (b, c))
,fmap assoc' :: f (a, (b, c)) -> f ((a, b), c)
。Monoidal
的方法来书写Applicative
,反之亦然。unit = pure ()
f ** g = (,) <$> f <*> g = liftA2 (,) f g
pure x = const x <$> unit
f <*> g = uncurry id <$> (f ** g)
liftA2 f x y = uncurry f <$> (x ** y)
Monoidal
和 Applicative
法则告诉我们同样的事情。我之前问过这个问题。
m a
具有结构m
和值a
。pure :: a -> m a
使我们能够构造一个最小的结构(类似于m
的恒等元),而(<*>) :: m (a -> b) -> m a -> m b
则使我们能够组合两个m
(类似于幺半群的操作),同时对内部值进行函数应用(一个值是a -> b
,另一个是a
,你得到一个b
)。 - Alec