2016-10-20 3 views
8

को देखते हुए:साफ़ `मामले से class`` Option` क्षेत्रों

case class Foo(a: Option[Int], b: Option[Int], c: Option[Int], d: Option[Int]) 

मैं केवल निर्माण अनुमति देना चाहते हैं एक Fooकेवल अगर अपने तर्कों का कम से कम एक Some, यानी नहीं सभी क्षेत्रों है None हैं।

यह प्रत्येक संस्करण के लिए एक बीजीय डेटा प्रकार लिखना, और फिर बनाने के लिए उप-वर्गों काफी कोड का एक सा होगा:

sealed trait Foo 
case class HasAOnly(a: Int)  extends Foo 
case class HasAB(a: Int, b: Int) extends Foo 
// etc... 

वहाँ एक क्लीनर, यानी कम कोड, मेरी समस्या का समाधान करने और रास्ता नहीं है shapeless का उपयोग कर?

उत्तर

5

sealed abstract case class चाल जो रोब नोरिस हाल ही में publicised, आप अपने Foo मामले वर्ग की विशेषताओं रख सकते लेकिन यह भी अपने स्वयं के स्मार्ट निर्माता है जो एक Option[Foo] दिए गए तर्कों के अपने सभी मानदंड पर खरे उतर या नहीं इस पर निर्भर करते रिटर्न प्रदान करने के लिए धन्यवाद :

sealed abstract case class Foo(
    a: Option[Int], b: Option[Int], c: Option[Int], d: Option[Int]) 

object Foo { 
    private class Impl(
    a: Option[Int], b: Option[Int], c: Option[Int], d: Option[Int]) 
    extends Foo(a, b, c, d) 

    def apply(
    a: Option[Int], 
    b: Option[Int], 
    c: Option[Int], 
    d: Option[Int]): Option[Foo] = 
    (a, b, c, d) match { 
     case (None, None, None, None) => None 
     case _ => Some(new Impl(a, b, c, d)) 
    } 
} 
6

आप नेस्टेड Ior के साथ कुछ इस तरह कर सकते हैं:

import cats.data.Ior 

case class Foo(iors: Ior[Ior[Int, Int], Ior[Int, Int]]) { 
    def a: Option[Int] = iors.left.flatMap(_.left) 
    def b: Option[Int] = iors.left.flatMap(_.right) 
    def c: Option[Int] = iors.right.flatMap(_.left) 
    def d: Option[Int] = iors.right.flatMap(_.right) 
} 

अब यह सब None रों के साथ एक Foo निर्माण करने के लिए असंभव है। आप केस क्लास कन्स्ट्रक्टर को निजी भी बना सकते हैं और Ior साथी ऑब्जेक्ट पर वैकल्पिक कन्स्ट्रक्टर में तर्क होता है, जो पैटर्न को थोड़ा अच्छे से मेल खाता है, लेकिन यह उदाहरण को थोड़ी देर तक भी बना देगा।

दुर्भाग्यवश यह उपयोग करने के लिए एक प्रकार का गुंजाइश है। आप वास्तव में क्या चाहते हैं Ior का एक सामान्यीकरण है जिस तरह shapeless.CoproductEither का सामान्यीकरण है। हालांकि, मुझे इस तरह की किसी भी चीज़ के तैयार संस्करण के बारे में व्यक्तिगत रूप से पता नहीं है।

0

मैं आपकी कक्षा के लिए एक निर्माता पैटर्न प्रदान करने की अनुशंसा करता हूं। यह विशेष रूप से उपयोगी होता है यदि आपकी लाइब्रेरी के उपयोगकर्ता आमतौर पर केवल कई वैकल्पिक पैरामीटर निर्दिष्ट करते हैं। और पैरामीटर प्रति अलग तरीकों के साथ एक बोनस के रूप में वे अगर यह पूरा हो Some

में सब कुछ रैप करने के लिए आप को चिह्नित करने के वर्ग पर एक ही प्रकार के पैरामीटर का उपयोग कर सकते हैं की जरूरत नहीं होगी और आप (यानी कम से कम एक Some पैरामीटर है) अंतर्निहित पैरामीटर के साथ बिल्ड विधि पर इसे लागू कर सकते हैं।

sealed trait Marker 
trait Ok extends Marker 
trait Nope extends Markee 

case class Foo private(a: Option[Int], b: Option[Int], c: Option[Int], d: Option[Int]) 

object Foo{ 
    case class Builder[T <: Marker](foo: Foo){ 
    def a(x:Int) = Builder[Ok](foo = foo.copy(a=Some(x))) 
    def b(x:Int) = Builder[Ok](foo = foo.copy(b=Some(x))) 
    // ... 

    def build(implicit ev: T <:< Ok) = foo 
    } 

    def create = Builder[Nope](Foo(None, None, None, None)) 
} 

मैंने पहले एक प्रकार से सुरक्षित निर्माता के साथ प्रयोग किया है। इस जिस्ट का एक और जटिल उदाहरण है, हालांकि यह भी ट्रैक रखता है कि कौन सा फ़ील्ड सेट किया गया है, ताकि इसे बिना किसी असुरक्षित कॉल के Option.get पर निकाला जा सके। https://gist.github.com/gjuhasz86/70cb1ca2cc057dac5ba7

संबंधित मुद्दे