2011-10-16 26 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) 
} 

ここでは、整数のリストを返す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#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]の暗黙の値を見つけることができないというエラーが表示されます。だから、明らかに暗黙に依存メソッドタイプのために働く方法について別の何かがあります。)私はここで何をしたい達成トリックを指摘し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

私は通常、あなたは自分の文法(Parsers)でそれをミックスする必要が続いて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 
    } 
} 

のためにミックスインでParserに暗黙の拡張子を追加し、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 [文法パーザ]であり、文法パラメータではこれは私たちのものではありません。

私の答えはになります。はできませんが、誰かが何かを考え出すことができたら驚くことはありません。

2

パス依存型を扱うのは難しいです。ここでは一つの方法です:

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

+0

これは賢いと私の依存方法の種類のバージョンよりもはるかに進歩しているが、私はまだその '暗黙のヴァルMなしでやってみたい:モナド[パーサ] = parserMonad(testParser)' 。それだけでは不可能だと思いますか? –

+1

「成功」を呼び出すには、「パーサー」のインスタンスを作成する必要があります。あなたはそれを 'parserMonad'に与えるために' parser'自体を暗黙的にすることができますが、それはよい考えのようには聞こえません。 – retronym

+1

'asInstanceOf'を認める場合は、実際にこれを行うことができます:https://github.com/retronym/scalaz7-experimental/commit/aa80e4792799a509c728eecff771ec74518720e7 – retronym

関連する問題