好了,我知道什么是Applicative
型类包含的,为什么这是有用的。 但我不能完全环绕你如何使用它在非简单的例子,我的大脑。
举个例子,下面很简单秒差距解析器:
integer :: Parser Integer
integer = do
many1 space
ds <- many1 digit
return $ read ds
现在,如何赫克你会写,如果没有使用Monad
实例Parser
? 很多人声称,这是可以做到,是一个不错的主意,但我无法弄清楚究竟是如何。
integer :: Parser Integer
integer = read <$> (many1 space *> many1 digit)
要么
integer = const read <$> many1 space <*> many1 digit
不管你认为其中任一更易读是你。
我会写
integer :: Parser Integer
integer = read <$ many1 space <*> many1 digit
有一堆左结合(如应用程序)语法分析器建设运营的<$>
<*>
<$
<*
在最左边的事应是纯功能是组装从元件值的结果值。 每个运营商右边的东西应该是一个分析器,集体给左到右的语法成分。 其中运营商使用取决于两种选择,如下所示。
the thing to the right is signal / noise
_________________________
the thing to the left is \
+-------------------
pure / | <$> <$
a parser | <*> <*
所以,具有选择read :: String -> Integer
作为将要递送解析器的语义的纯函数,我们可以前导间隔作为“噪声”和数字作为“信号”的一群进行分类,因此
read <$ many1 space <*> many1 digit
(..) (.........) (.........)
pure noise parser |
(.................) |
parser signal parser
(.................................)
parser
您可以结合使用多种可能性
p1 <|> ... <|> pn
并表示与不可能
empty
这是很少有必要在解析器名称组件,并将得到的代码看起来更像是附加了语义语法。
您的例子可以逐步改写到更清晰类似于一个应用型的一种形式:
do
many1 space
ds <- many1 digit
return $ read ds
定义do
记号:
many1 space >> (many1 digit >>= \ds -> return $ read ds)
的定义$
:
many1 space >> (many1 digit >>= \ds -> return (read ds))
的定义.
:
many1 space >> (many1 digit >>= (return . read))
第三个单子法(关联性):
(many1 space >> many1 digit) >>= (return . read)
的定义liftM
(在非do
记号):
liftM read (many1 space >> many1 digit)
这是(或者应该是,如果我没有搞砸了:))的行为,你的例子是相同的。
现在,如果你更换liftM
与fmap
与<$>
和>>
与*>
你会得到应用型:
read <$> (many1 space *> many1 digit)
这是有效的,因为liftM
, fmap
和<$>
一般应该是同义词,因为都是>>
和*>
这一切工作,我们可以做到这一点,因为原来的例子没有使用任何解析器的结果,建立一个下解析器。