2011-10-16 42 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