2011-10-16 44 views
9

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#ParserApplicative实例。当然,下面的天真的方法是行不通的,因为我们需要的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。这是因为演员的作弊,但仍然非常整齐。

回答

4

我通常混入隐含的扩展添加到ParserParsers

trait BindForParser extends Parsers { 
    implicit def ParserBind = new Bind[Parser] { 
    def bind[A,B](p: Parser[A], f: A => Parser[B]) = p flatMap f 
    } 
} 

然后你只需混合,在你的语法(Parsers),并作为Parser情况下,通常只内Parsers操纵,没有在语法完成后,你不能再混合某些东西了。在你的例子中,你只需要做

object parser extends Parsers with BindForParser 

在更普遍的问题,是否有可能做“从外面”,最直接的方式很可能是这样的

implicit def ParserBind(grammar: Parsers) = new Bind[grammar.Parser] { 
    def bind[A,B](p: grammar.Parser[A], f: A => grammar.Parser[B]) = p flatMap f 
} 

但是,这是不允许的,方法参数(在这里grammar)不被视为稳定的标识符,因此grammar.Parser不允许作为类型。然而,可以通过选项-Xexperimental。但即使如此,我也不明白隐含的内容在需要时会如何启动。我们想要的是一个隐含的Bind [grammar.Parser],并且语法参数不是我们所拥有的。

所以我的回答是它不能完成,但如果有人能想出一些东西我不会感到惊讶。

2

处理路径依赖类型非常棘手。这里有一种方法:

https://github.com/retronym/scalaz7-experimental/commit/8bf1d2a090cf56d33e11c554e974ea3c82b7b37f

+0

这是聪明,比我依赖的方法类型的版本好多了,但我还是想不说'隐VAL M至事:单子[分析器] = parserMonad(testParser)' 。你认为这是不可能的吗? –

+1

我需要'Parsers'的实例来调用'success'。你可以让“解析器”本身隐式为它提供'parserMonad',但这听起来不是一个好主意。 – retronym

+1

如果你愿意承认一个'asInstanceOf',你实际上可以做到这一点:https://github.com/retronym/scalaz7-experimental/commit/aa80e4792799a509c728eecff771ec74518720e7 – retronym