9

この最近の Stack Overflow questionで、作成者は、あるタイプのパーサーのリストを、そのタイプのリストを返すパーサーに変更したいと考えていました。Scalaz のsequencefor 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を取得する方法については明確ではありません。のインスタンスが同じである必要があるため、次の素朴なアプローチはもちろん機能しません。ApplicativeParsers#ParserParsers

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。キャストのせいでちょっとごまかしているが、それでもかなりきちんとしている.

4

2 に答える 2

4

Parser私は通常、ミックスインで暗黙的な拡張を追加しますParsers

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でのみ操作されるため、後で文法が完了し、何かを混ぜることができなくなったときに mixin が必要になる可能性はあまりありません。たとえば、あなたはただやります

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] であり、文法パラメーターを使用すると、これは私たちが持っているものではありません。

だから私の答えはそれはできないということですが、誰かが何かを思いついたとしても、私はそれほど驚かないでしょう.

于 2011-10-16T18:41:54.827 に答える
2

パスに依存する型を扱うのはかなりトリッキーです。1 つの方法を次に示します。

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

于 2011-10-16T19:00:04.423 に答える