在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