【发布时间】:2017-03-27 04:58:57
【问题描述】:
当我比较 Applicative 和 Monad 类型类的二元运算时
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(=<<) :: Monad m => (a -> m b) -> m a -> m b
两个区别变得明显:
-
ap需要一个普通的纯函数,而bind需要一个单子动作,它必须返回一个相同类型的单子 -
ap的动作顺序由应用程序决定,而bind的一元动作可以决定控制流
所以单子给了我额外的表达能力。然而,由于它们不再接受普通的纯函数,这种表现力似乎是以牺牲代码重用为代价的。
我的结论可能有点幼稚甚至是错误的,因为我对 Haskell 和 monadic 计算几乎没有经验。黑暗中的任何光明都值得赞赏!
【问题讨论】:
-
你说的“代码重用”是什么意思? Monads 和 Applicatives 是在不同上下文中使用的不同概念。虽然每个 Monad 也是一个应用程序,但使用应用程序可以生成更高效的代码,例如 Parsers。
-
有了仿函数,我可以在特定的计算上下文中重用普通的纯函数。使用应用程序,我可以重用从同一个应用程序的多个上下文中获取值的普通纯函数。现在我想在单子计算中重用普通的纯函数。但是
bind需要一个“特殊”函数(我称之为单子动作),它必须返回一个相同类型的单子。所以我的印象是,纯函数的重用并不是 monad 的目的。 -
我无法理解前提。
ap不采用函数a -> b,但f (a -> b)是另一回事。当然,您可以使用ap (pure g) ...,但那是另一回事。此外,每个 monad 都是一个应用程序,所以如果你可以将类型T a变成一个 monad,那么没有理由不把它变成一个应用程序(事实上,这在最近的 GHC 中是强制性的......)因此,我不能理解这个问题。 -
@ftor (1)
f (a -> b)值不一定是上下文中的单个纯函数——例如,考虑[(2*), (3+)] :: Num a => [a -> a]或Nothing :: Maybe (String -> String)。虽然它确实由上下文中的(零个或多个)普通函数组成,但称其为“纯函数”会导致混淆。 (2)(=<<) :: Monad m => (a -> m b) -> m a -> m b的一个关键特性是m b的上下文取决于a值。在putStrLn =<< getLine中,屏幕上显示的字符串与您在终端中键入的字符串完全相同。fmap和(<*>)不能这样做。
标签: haskell functional-programming monads dry reusability