【发布时间】:2011-10-16 16:46:12
【问题描述】:
在this recent Stack Overflow question 中,作者想将某种类型的解析器列表更改为返回该类型列表的解析器。我们可以想象使用 Scalaz 的 sequence 来实现应用函子:
import scala.util.parsing.combinator._
import scalaz._
import Scalaz._
object parser extends RegexParsers {
val parsers = List(1, 2, 3).map(repN(_, """\d+""".r))
def apply(s: String) = parseAll(parsers.sequence, s)
}
这里我们获取一个包含三个返回整数列表的解析器的列表,并将其转换为一个返回整数列表的解析器。不幸的是,Scalaz 没有为Parser 提供Applicative 实例,因此这段代码无法编译,但这很容易修复:
import scala.util.parsing.combinator._
import scalaz._
import Scalaz._
object parser extends RegexParsers {
val parsers = List(1, 2, 3).map(repN(_, """\d+""".r))
def apply(s: String) = parseAll(parsers.sequence, s)
implicit def ParserPure: Pure[Parser] = new Pure[Parser] {
def pure[A](a: => A) = success(a)
}
implicit def ParserFunctor: Functor[Parser] = new Functor[Parser] {
def fmap[A, B](p: Parser[A], f: A => B) = p.map(f)
}
implicit def ParserBind: Bind[Parser] = new Bind[Parser] {
def bind[A, B](p: Parser[A], f: A => Parser[B]) = p.flatMap(f)
}
}
这按预期工作:例如,parser("1 2 3 4 5 6") 给了我们List(List(1), List(2, 3), List(4, 5, 6))。
(我知道我可以只给出一个Apply 实例,但Bind 实例更简洁。)
每次我们扩展Parsers 时不必这样做会很好,但我不清楚如何更一般地为Parsers#Parser 获取Applicative 实例。以下幼稚的方法当然行不通,因为我们需要 Parsers 的实例相同:
implicit def ParserBind: Bind[Parsers#Parser] = new Bind[Parsers#Parser] {
def bind[A, B](p: Parsers#Parser[A], f: A => Parsers#Parser[B]) = p.flatMap(f)
}
我很清楚这应该是可能的,但我对 Scala 的类型系统不够熟悉,不知道如何去做。我缺少什么简单的东西吗?
针对以下答案:我确实尝试了-Ydependent-method-types 路线,并且走到了这一步:
implicit def ParserApplicative(g: Parsers): Applicative[g.Parser] = {
val f = new Functor[g.Parser] {
def fmap[A, B](parser: g.Parser[A], f: A => B) = parser.map(f)
}
val b = new Bind[g.Parser] {
def bind[A, B](p: g.Parser[A], f: A => g.Parser[B]) = p.flatMap(f)
}
val p = new Pure[g.Parser] {
def pure[A](a: => A) = g.success(a)
}
Applicative.applicative[g.Parser](p, FunctorBindApply[g.Parser](f, b))
}
问题(正如didierd 指出的那样)是不清楚如何让implicit 发挥作用。所以这种方法确实有效,但您必须在语法中添加如下内容:
implicit val applicative = ParserApplicative(this)
此时,mixin 方法显然更具吸引力。
(作为旁注:我希望能够在上面简单地编写Applicative.applicative[g.Parser],但这会给出一个错误,指出编译器找不到Pure[g.Parser] 的隐式值——即使旁边坐着一个很明显,对于依赖方法类型的隐式工作方式有些不同。)
感谢retronym 指出一个实现我想要的技巧。我从his code中提取了以下内容:
implicit def parserMonad[G <: Parsers with Singleton] =
new Monad[({ type L[T] = G#Parser[T] })#L] {
def pure[A](a: => A): G#Parser[A] = {
object dummy extends Parsers
dummy.success(a).asInstanceOf[G#Parser[A]]
}
def bind[A, B](p: G#Parser[A], f: (A) => G#Parser[B]): G#Parser[B] =
p.flatMap(f)
}
如果你有这个范围,你会在任何扩展 Parsers 的对象中获得一个 Parser 的 monad 实例。由于演员阵容,这有点作弊,但仍然很整洁。
【问题讨论】: