我正在阅读有关应用程序并尝试理解它的haskellbook.
在书中,作者提到:
因此,使用Applicative,我们的结构和功能应用为我们的价值观提供了Monoid!
monoid如何连接到applicative?
Zet*_*eta 10
备注:我还没有这本书(II)和IIRC,至少有一位作者在SO上活跃,应该能够回答这个问题.话虽这么说,monoid(或者更确切地说是半群)背后的想法是你有办法从monoid 1中的两个对象创建另一个对象:
mappend :: Monoid m => m -> m -> m
Run Code Online (Sandbox Code Playgroud)
那么Applicative幺半群怎么样?嗯,正如你的报价所说,它的结构是一个幺半群.也就是说,我们从一个开始f something,继续f anotherthing,然后我们得到,你猜对了f resulthing:
amappend :: f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)
在我们继续之前,短暂的,很短的时间内,让我们忘记f那种善意* -> *.我们最终得到了什么?
amappend :: f -> f -> f
Run Code Online (Sandbox Code Playgroud)
那是"单体结构"的一部分.这就是Haskell Applicative和FunctorHaskell 之间的区别,因为Functor我们没有这个属性:
fmap :: (a -> b) -> f a -> f b
-- ^
-- no f here
Run Code Online (Sandbox Code Playgroud)
这也是我们遇到麻烦的原因,如果我们只尝试使用(+)或其他功能fmap:在fmap我们被卡住之后,除非我们能以某种方式在新结构中应用我们的新功能.这将我们带到您问题的第二部分:
所以,使用Applicative,我们有我们价值观的功能应用!
功能应用是($).如果我们看一下<*>,我们可以立即看到它们是相似的:
($) :: (a -> b) -> a -> b
(<*>) :: f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)
如果我们忘记了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
Run Code Online (Sandbox Code Playgroud)
我想,这就是作者对"功能应用"的意思.我们突然可以将那些隐藏在我们结构中的函数带到我们的结构中的其他值上.由于单一性质,我们留在那个结构中.
话虽这么说,我个人永远不会称那个单调,因为<*>不对同一类型的两个参数进行操作,并且应用程序缺少空元素.
1对于一个真正的半群/幺半群,该操作应该是关联的,但这在这里并不重要