2013-07-02 4 views
7

के अंदर एक टुपल कैसे वापस करें मैं स्कालज़ 7 के ईटीटीटी का उपयोग कर रहा हूं ताकि समझदारी के लिए राज्य और \ /। अब तक सब ठीक है; मैं कुछ मूल रूप से है कि मिल:एक ईटटी

State[MyStateType, MyLeftType \/ MyRightType] 

और यह मेरे लिए-comprehensions < की बाईं ओर अच्छा चर है कि निर्माण करने के लिए अनुमति देता है -।

लेकिन मैं यह नहीं समझ सकता कि राज्य कार्रवाई से टुपल कैसे वापस करें। एकल परिणाम ठीक हैं - नीचे दिए गए कोड में, "वैल समझ" बिल्कुल वही है जो मैं करना चाहता हूं।

लेकिन जब मैं एक ट्यूपल वापस करना चाहता हूं तो चीजें अलग हो जाती हैं; "वैल otherComprehension" मुझे

(a, b) <- comprehension 

कर ऐसा लगता है कि यह \ के बाईं ओर उम्मीद/एक monoid होने के लिए और मैं क्यों समझ में नहीं आता नहीं दूँगा। मैं क्या खो रहा हूँ?

(Scalaz 7 2.0.0-स्नैपशॉट, स्काला 2.10.2)

object StateProblem { 
    case class MyStateType 
    case class MyRightType 
    case class MyLeftType 

    type StateWithFixedStateType[+A] = State[MyStateType, A] 
    type EitherTWithFailureType[F[+_], A] = EitherT[F, MyLeftType, A] 
    type CombinedStateAndFailure[A] = EitherTWithFailureType[StateWithFixedStateType, A] 

    def doSomething: CombinedStateAndFailure[MyRightType] = { 
    val x = State[MyStateType, MyLeftType \/ MyRightType] { 
     case s => (s, MyRightType().right) 
    } 
    EitherT[StateWithFixedStateType, MyLeftType, MyRightType](x) 
    } 

    val comprehension = for { 
    a <- doSomething 
    b <- doSomething 
    } yield (a, b) 

    val otherComprehension = for { 
    // this gets a compile error: 
    // could not find implicit value for parameter M: scalaz.Monoid[com.seattleglassware.StateProblem.MyLeftType] 
    (x, y) <- comprehension 

    z <- doSomething 
    } yield (x, y, z) 
} 

संपादित करें: मैं सबूत है कि MyLeftType, एक इकाई है, भले ही यह नहीं है जोड़ दिया है। मेरा असली कोड में, MyLeftType एक मामले वर्ग (EarlyReturn कहा जाता है) है, इसलिए मैं एक शून्य प्रदान कर सकते हैं, लेकिन संलग्न तभी काम करता है तर्कों में से एक एक शून्य है:

implicit val partialMonoidForEarlyReturn = new Monoid[EarlyReturn] { 
    case object NoOp extends EarlyReturn 
    def zero = NoOp 
    def append(a: EarlyReturn, b: => EarlyReturn) = 
     (a, b) match { 
     case (NoOp, b) => b 
     case (a, NoOp) => a 
     case _   => throw new RuntimeException("""this isnt really a Monoid, I just want to use it on the left side of a \/""") 
     } 
    } 

मैं इस आश्वस्त नहीं कर रहा हूँ एक है अच्छा विचार है, लेकिन यह समस्या को हल कर रहा है।

for { 
    //(x, y) <- comprehension 
    p <- comprehension 

    z <- doSomething 
} yield (p._1, p._2, z) 

या शायद थोड़ा बेहतर

for { 
    //(x, y) <- comprehension 
    p <- comprehension 
    (x, y) = p 

    z <- doSomething 
} yield (x, y, z) 

यह बहुत अच्छा नहीं है, लेकिन काम करता है:

+1

रास्ते में कुछ अजीब चल रहा है 2.10.1+ यहां 'समझ' के लिए 'इस सवाल] (http://stackoverflow.com/q/17424763/334519) के सरलीकृत संस्करण के लिए desugars वही मुद्दा। –

+2

और स्पष्ट होने के लिए, ऐसा इसलिए हो रहा है क्योंकि 'EitherT' (या' \/') को फ़िल्टर करने के लिए बाईं तरफ एक मोनोइड आवृत्ति की आवश्यकता होती है, और किसी कारण से 2.10.2 इस 'समझ' में फ़िल्टर ऑपरेशन चिपका रहा है। –

उत्तर

4

मैं ऊपर एक टिप्पणी में ध्यान दें के रूप में, समस्या यह है कि अपने दूसरे for -comprehension की desugared संस्करण एक शामिल है 2.10.2 (और 2.10.1, लेकिन 2.10.0) में फ़िल्टरिंग ऑपरेशन, और बाएं तरफ के प्रकार के लिए एक मोनॉयड उदाहरण के बिना EitherT (या सादा पुराना \/) फ़िल्टर करना संभव नहीं है।

val x: String \/ Int = 1.right 
val y: String \/ Int = x.filter(_ < 0) 

y क्या है:

यह देखने के लिए क्यों monoid निम्न उदाहरण में आवश्यक है बहुत आसान है? यह स्पष्ट है कि इसे किसी प्रकार का "खाली" String \/ Int होना चाहिए, और \/ सही पक्षपातपूर्ण है, हम जानते हैं कि यह उस तरफ एक मूल्य नहीं हो सकता है। इसलिए हम बाईं ओर के लिए एक शून्य की जरूरत है, और String के लिए monoid उदाहरण इस-यह प्रदान करता है सिर्फ रिक्त स्ट्रिंग है: व्यवहार कर रहे हैं

assert(y == "".left) 

this answer के अनुसार my related question टपल पैटर्न के बारे में करने के लिए for -comprehensions में, 2.10.2 में देखकर सही और इरादा है- withFilter पर स्पष्ट रूप से पूरी तरह से अनावश्यक कॉल रहने के लिए यहां है।

आप पेट्र Pudlák के जवाब में समाधान का उपयोग कर सकते हैं, लेकिन यह भी ध्यान देने योग्य निम्नलिखित शुगर फ्री संस्करण भी बहुत स्पष्ट और संक्षिप्त है कि लायक है:

val notAnotherComprehension = comprehension.flatMap { 
    case (x, y) => doSomething.map((x, y, _)) 
} 

यह कम या ज्यादा क्या मैं भोलेपन उम्मीद होती है for - किसी भी तरह से desugar करने के लिए समझ (और मैं not the only one हूँ)।

2

कारण जानने के बिना, मैं एक संभावित समाधान मिल गया।

(मैं वास्तव में सराहना करते हैं कि आप एक आत्म निहित, समस्या का काम कर रहे उदाहरण बना दिया।)

+1

x = p._1 के बजाय; वाई = पी ._2; आप बस कर सकते हैं (एक्स, वाई) = पी। अजीब लगता है कि आपको इसे दूसरी लाइन पर करने की ज़रूरत है (@TravisBrown से लिंक और टिप्पणी उपयोगी जानकारी है)। –

+0

@JamesMoore True, corrected। –