首先是简单的部分——“奇怪的”导入系统:
import scalaz._, Scalaz._
就这样。这将始终有效(当然,除非名称冲突,但这是任何库的可能性并且很容易解决),并且没有人会因为不使用主要是关于文档的新的 à la carte 导入而看不起你。
monad 转换器的基本模式是你有一个WhateverT[F[_], ...]
类型类,它有一个名为runWhateverT
(或只是run
)返回一个F[Whatever[...]]
.
在这种情况下,您似乎想要以 结尾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)
}
有关我们为什么需要这条线的一些讨论,请参阅我的问题here 。implicit val monad = ...
它是这样工作的:
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]]
解析一串数字并返回 a Some(i)
(如果它们表示奇数)或 a None
(如果它们是偶数)。
但是,在这种情况下,我们实际上不会调用oddNumber.run
—— 我们使用 monad 转换器这一事实意味着我们可以在单个理解中组合oddNumber
提升Parser
的动作( here)。literal(...)
for
这里的语法很难看——例如,我们失去了漂亮的~
组合子和从String
to的隐式转换Parser[String]
,但如果我们愿意,我们可以轻松编写这些东西的提升版本。