2011-10-16 11 views
9

में नेस्टेड कक्षाओं के लिए लेखन प्रकार वर्ग उदाहरण this recent Stack Overflow question में, लेखक किसी प्रकार के पार्सर्स की एक सूची को एक पार्सर में बदलना चाहता था जो उस प्रकार की सूचियों को लौटाता है। हम अनुप्रयोगी functors के लिए Scalaz के sequence के साथ ऐसा करने कल्पना कर सकते हैं:स्कैला

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 के लिए आम तौर पर कैसे प्राप्त करें। निश्चित रूप से निम्नलिखित अनुभवहीन दृष्टिकोण काम नहीं करता है, क्योंकि हम 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) 
} 

यह बहुत मेरे लिए स्पष्ट है कि यह संभव होना चाहिए है, लेकिन मैं स्काला के साथ काफी सहज महसूस नहीं कर रहा हूँ यह जानने के लिए सिस्टम टाइप करें कि इसके बारे में कैसे जाना है। क्या कुछ आसान है जो मुझे याद आ रही है?


नीचे जवाब के जवाब में: मैं -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) 
    } 

आप दायरे में इस है, तो आप एक इकाई उदाहरण Parser के लिए किसी भी Parsers विस्तार वस्तु में मिलता है। यह कास्ट की वजह से धोखाधड़ी की तरह है, लेकिन अभी भी बहुत साफ है।

उत्तर

4

मैं आमतौर पर निहित विस्तार Parser को mixins में के लिए 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 अंदर छेड़छाड़ कर रहे हैं, वहाँ नहीं कर रहे हैं बाद में मिश्रण की आवश्यकता होगी, जब व्याकरण किया जाता है और आप अब कुछ मिश्रण नहीं कर सकते हैं। आपके उदाहरण में, आप बस

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 के साथ संभव है। लेकिन फिर भी, मैं नहीं देखता कि आवश्यकता होने पर निहित कैसे लाएगा। हम जो चाहते हैं वह एक निहित बाइंड [व्याकरण। पार्सर] है, और व्याकरण पैरामीटर के साथ यह हमारे पास नहीं है।

तो मेरा उत्तर होगा यह नहीं किया जा सकता है, लेकिन अगर कोई कुछ के साथ आ सकता है तो मैं आश्चर्यचकित नहीं होगा।

2

पथ निर्भर प्रकारों से निपटना बहुत मुश्किल है। यहाँ एक तरीका है:

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

+0

यह चालाक और मेरे निर्भर विधि प्रकार संस्करण की तुलना में बहुत अच्छे है, लेकिन मैं अभी भी है कि 'निहित वैल एम के बिना क्या करना चाहते हैं: इकाई [पार्सर] = parserMonad (testParser)' । क्या आपको लगता है कि यह संभव नहीं है? –

+1

मुझे 'सफलता' कॉल करने के लिए 'पार्सर्स' का उदाहरण होना चाहिए। आप 'पार्सर' को इसे 'पार्सरमोनाड' फ़ीड करने के लिए खुद को निहित कर सकते हैं, लेकिन यह एक अच्छा विचार नहीं लगता है। – retronym

+1

यदि आप 'asInstanceOf' को स्वीकार करने के इच्छुक हैं, तो आप वास्तव में यह कर सकते हैं: https://github.com/retronym/scalaz7-experimental/commit/aa80e4792799a509c728eecff771ec74518720e7 – retronym