在this recent Stack Overflow question中,作者希望将某种类型的分析器列表更改为返回该类型列表的分析器。我们可以Scalaz的sequence
想象一下这样的应用性函子:在Scala中编写嵌套类的类实例
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)
在这一点上混在方法上显然更具吸引力。 (作为一个方面说明:我希望能够简单地编写上面的Applicative.applicative[g.Parser]
,但是这给出了一个错误,说编译器找不到Pure[g.Parser]
的隐式值 - 尽管坐在它旁边。所以很明显有一些关于相关方法类型的方式implicits的工作不同。)
感谢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
。这是因为演员的作弊,但仍然非常整齐。
这是聪明,比我依赖的方法类型的版本好多了,但我还是想不说'隐VAL M至事:单子[分析器] = parserMonad(testParser)' 。你认为这是不可能的吗? –
我需要'Parsers'的实例来调用'success'。你可以让“解析器”本身隐式为它提供'parserMonad',但这听起来不是一个好主意。 – retronym
如果你愿意承认一个'asInstanceOf',你实际上可以做到这一点:https://github.com/retronym/scalaz7-experimental/commit/aa80e4792799a509c728eecff771ec74518720e7 – retronym