この最近の Stack Overflow questionで、作成者は、あるタイプのパーサーのリストを、そのタイプのリストを返すパーサーに変更したいと考えていました。Scalaz のsequence
for applicative functor でこれを行うことを想像できます:
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)
}
ここでは、整数のリストを返す 3 つのパーサーのリストを取得し、それを整数のリストのリストを返すパーサーに変換します。残念ながら、Scalaz は のApplicative
インスタンスを提供していないためParser
、このコードはコンパイルされませんが、簡単に修正できます。
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
を取得する方法については明確ではありません。のインスタンスが同じである必要があるため、次の素朴なアプローチはもちろん機能しません。Applicative
Parsers#Parser
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))
}
問題は (ディディアードが指摘するように) を開始する方法が不明なことimplicit
です。したがって、このアプローチは機能しますが、文法に次のようなものを追加する必要があります。
implicit val applicative = ParserApplicative(this)
その時点で、ミックスインのアプローチは明らかにはるかに魅力的です。
(補足として、上記のように簡単に書けると思っていましたが、コンパイラが - のすぐ隣にあるにもかかわらずApplicative.applicative[g.Parser]
、暗黙の値を見つけることができないというエラーが表示されます。Pure[g.Parser]
従属メソッド型に対する暗黙の動作方法)。
ここで私が望むことを達成するトリックを指摘してくれたretronymに感謝します。私は彼のコードから以下を抽象化しました:
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)
}
これがスコープ内にある場合、 をParser
拡張する任意のオブジェクトでモナドのインスタンスを取得しますParsers
。キャストのせいでちょっとごまかしているが、それでもかなりきちんとしている.