我有一个monadic类型Exp
,我想构建一个解析为这样一个值的解析器。以下代码有效,但是我能做些更好/更酷的事情吗?
def grid(x: Int, y: Int): Problem = ???
def expInt: Parser[Exp[Int]] = ???
def grid: Parser[Exp[Problem]] =
for{
ex ~ _ ~ ey <- "grid(" ~> expInt ~ "," ~ expInt <~ ")"
} yield for{
x <- ex
y <- ey
} yield grid(x,y)
我听说过monad变形金刚,但仍然有点害怕Scalaz 7的奇怪的导入系统。有人可以回答是否
Exp
Monad的Scala组合器解析器。答案 0 :(得分:6)
首先是简单的部分 - “奇怪的”导入系统:
import scalaz._, Scalaz._
这就是全部。这将永远有效(当然,除非是名称冲突,但这对任何库来说都是可能的并且易于解决),没有人会因为没有使用新的单点菜单导入而瞧不起你,这主要是关于文档的。
monad变换器的基本模式是你有一个WhateverT[F[_], ...]
类型类,它有一个名为runWhateverT
(或只是run
)的方法,它返回F[Whatever[...]]
。< / p>
在这种情况下,您似乎希望得到Parser[Exp]
,这表明您需要自己的ExpT[F[_]]
转换器。我不会从有关如何实现它的细节开始(当然,这将取决于你的monad的语义),但是将使用Scalaz的OptionT
给出一个例子:
import scala.util.parsing.combinator._
import scalaz._, Scalaz._
object MyParser extends RegexParsers {
implicit val monad = parserMonad(this)
def oddNumber: OptionT[Parser, Int] = OptionT.optionT(
"\\d+".r ^^ (_.toInt) ^^ (i => (i % 2 != 0) option i)
)
def pairOfOdds: OptionT[Parser, (Int, Int)] = for {
_ <- literal("(").liftM[OptionT]
x <- oddNumber
_ <- literal(",").liftM[OptionT]
y <- oddNumber
_ <- literal(")").liftM[OptionT]
} yield (x, y)
def apply(s: String) = parse(pairOfOdds.run, s)
}
有关我们需要implicit val monad = ...
行的原因的讨论,请参阅my question here。
它的工作原理如下:
scala> MyParser("(13, 43)")
res0: MyParser.ParseResult[Option[(Int, Int)]] = [1.9] parsed: Some((13,43))
scala> MyParser("(13, 42)")
res1: MyParser.ParseResult[Option[(Int, Int)]] = [1.8] parsed: None
请注意,oddNumber.run
会为我们提供一个Parser[Option[Int]]
来解析一串数字并返回Some(i)
(如果它们代表奇数)或None
(如果他们是偶数)。
我们实际上并没有打电话给oddNumber.run
,但在这种情况下 - 我们使用monad变换器意味着我们可以用oddNumber
组合Parser
单个literal(...)
理解中的动作(for
此处)。
这里的语法很丑陋 - 例如,我们失去了很好的~
组合器以及从String
到Parser[String]
的隐式转换,但是我们可以很容易地编写这些内容的提升版本我们想要。