【发布时间】:2014-12-16 11:35:07
【问题描述】:
所以我正在尝试编写一个复杂的解析器,只使用 Applicative(有问题的解析器甚至根本没有实现 Monad)。
对于简单的解析器,这很容易。对于不平凡的人......不是那么多。应用程序界面似乎强烈地迫使您以无点风格编写所有内容。这是极难处理的。
考虑,例如:
call = do
n <- name
char '('
trim
as <- sepBy argument (char ',' >> trim)
char ')'
trim
char '='
r <- result
return $ Call {name = n, args = as, result = r}
现在让我们尝试使用 applicative 来写:
call =
(\ n _ _ as _ _ _ _ r -> Call {name = n, args = as, result = r}) <$>
name <*>
char '(' <*>
trim <*>
sepBy argument (const const () <$> char ',' <*> trim) <*>
char ')' <*>
trim <*>
char '=' <*>
trim <*>
result
Applicative 强制我将变量绑定放置在离实际解析器很远的地方。 (例如,尝试确认as 确实绑定到sepBy argument ...;要验证我没有得到错误的_ 模式计数并不容易!)
另一个非常不直观的事情是<*> 将函数应用于一个值,但*> 和<* 只是纯粹的排序。这花了我 ages 的时间。不同的方法名称会使这一点更加清晰。 (但遗憾的是,Monad 似乎抓住了>> 和<<。)似乎这些可以堆叠,产生类似
exit =
"EXIT:" *>
trim *>
name <*
char '(' <*
trim <*
char ')' <*
trim
您可以做到这一点并不明显。而且,对我来说,这段代码真的不是很可读。更重要的是,我仍然没有弄清楚你如何处理收集多个值同时删除多个其他值。
总之,我发现自己希望我可以使用 do-notation!我实际上不需要根据先前的结果来改变效果;我不需要 Monad 的力量。但是这个符号更具可读性。 (我一直想知道实现这一点是否真的可行;你能从语法上判断一个特定的 do-block 何时可以机械地转换为 applicative 吗?)
有人知道解决这些问题的方法吗?尤其是,我怎样才能将变量绑定更靠近它们绑定的解析器?
【问题讨论】:
-
我想等待
Applicative-Do对某些人来说太难了;) -
我不认为
<<存在,至少在base中不存在。 -
@BartekBanachewicz Applicative-do... 功能请求已经存在。我想这是真的;如果你能想到的话,它已经在互联网的某个地方了。
-
这些都是很好的答案。我什至不确定选择哪一个作为“正确”答案...
-
来自未来的你好,
ApplicativeDo已登陆。
标签: parsing haskell applicative