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)
}
ここでは、整数のリストを返す3つのパーサのリストを取り、整数のリストのリストを返すパーサにそれを回します。残念ながら、このコードはコンパイルされませんので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]
の暗黙の値を見つけることができないというエラーが表示されます。だから、明らかに暗黙に依存メソッドタイプのために働く方法について別の何かがあります。)私はここで何をしたい達成トリックを指摘し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
のためのモナドのインスタンスを取得します。それはキャストのためにちょっとした不正行為だが、それでもかなりきちんとしている。
これは賢いと私の依存方法の種類のバージョンよりもはるかに進歩しているが、私はまだその '暗黙のヴァルMなしでやってみたい:モナド[パーサ] = parserMonad(testParser)' 。それだけでは不可能だと思いますか? –
「成功」を呼び出すには、「パーサー」のインスタンスを作成する必要があります。あなたはそれを 'parserMonad'に与えるために' parser'自体を暗黙的にすることができますが、それはよい考えのようには聞こえません。 – retronym
'asInstanceOf'を認める場合は、実際にこれを行うことができます:https://github.com/retronym/scalaz7-experimental/commit/aa80e4792799a509c728eecff771ec74518720e7 – retronym