【发布时间】:2013-02-19 00:15:39
【问题描述】:
Earlier 我询问了关于将一元代码翻译为仅使用 Parsec 的应用函子实例的问题。不幸的是,我收到了几个答复,回答了我真正提出的问题,但并没有真正给我太多的见解。所以让我再试一次......
总结我到目前为止的知识,应用函子是比单子更受限制的东西。在“少即是多”的传统中,限制代码可以做的事情增加了疯狂代码操作的可能性。无论如何,很多人似乎都相信,在可能的情况下,使用 applicative 代替 monad 是一种更好的解决方案。
Applicative 类在 Control.Applicative 中定义,其 Haddock 的列表有助于将类方法和实用程序函数与它们之间的大量类实例分开,从而难以同时快速查看屏幕上的所有内容。但相关的类型签名是
pure :: x -> f x
<*> :: f (x -> y) -> f x -> f y
*> :: f x -> f y -> f y
<* :: f x -> f y -> f x
<$> :: (x -> y) -> f x -> f y
<$ :: x -> f y -> f x
很有道理,对吧?
好吧,Functor 已经给了我们fmap,基本上就是<$>。即,给定从x 到y 的函数,我们可以将f x 映射到f y。 Applicative 添加了两个本质上新的元素。一个是pure,它的类型与return(以及各种范畴论类中的其他几个运算符)大致相同。另一个是<*>,它使我们能够获取一个函数容器和一个输入容器,并产生一个输出容器。
使用上面的操作符,我们可以非常巧妙地做一些事情,比如
foo <$> abc <*> def <*> ghi
这使我们可以采用 N 元函数并从 N 个函子中获取其参数,这种方式可以轻松推广到任何 N。
这点我已经明白了。主要有两件事我不了解。
首先,函数*>、<*和<$。从它们的类型来看,<* = const、*> = flip const 和 <$ 可能是类似的。大概这不描述了这些函数实际上做了什么。 (??!)
其次,在编写 Parsec 解析器时,每个可解析实体通常最终看起来像这样:
entity = do
var1 <- parser1
var2 <- parser2
var3 <- parser3
...
return $ foo var1 var2 var3...
由于应用函子不允许我们以这种方式将中间结果绑定到变量,我对如何为最后阶段收集它们感到困惑。为了理解如何做到这一点,我一直无法完全围绕这个想法。
【问题讨论】:
-
<$>和<$并非特定于Applicative。他们可以在任何Functor上工作。 -
“从他们的类型来看,
<*=const、*>=flip const和<$可能是类似的”(旁注,多年后)f x -> g y -> f x、f x -> g y -> g y等类型就是这种情况。
标签: haskell monads applicative