2016-04-25 5 views
7

मैं आसानी से सामान्य रूप से एक कोडेक इस तरह एक सील मामले वर्ग के परिवार के लिए निकाले जाते हैं कर सकते हैं:एक मोहरबंद मामले वर्ग के परिवार के लिए जादूगरनी कोडेक पाने जहां आधार विशेषता है एक (सील) टाइप सदस्य

import io.circe._ 
import io.circe.generic.auto._ 

sealed trait Base 
case class X(x: Int) extends Base 
case class Y(y: Int) extends Base 

object Test extends App { 
    val encoded = Encoder[Base].apply(Y(1)) 
    val decoded = Decoder[Base].apply(encoded.hcursor) 
    println(decoded) // Right(Y(1)) 
} 

हालांकि, अगर मैं जोड़ने आधार वर्ग मैं इसे अब और नहीं कर सकते हैं, भले ही वह एक सीलबंद विशेषता से घिरा रहा है करने के लिए एक प्रकार का सदस्य:

import io.circe._ 
import io.circe.generic.auto._ 

sealed trait Inner 
case class I1(i: Int) extends Inner 
case class I2(s: String) extends Inner 

sealed trait Base { type T <: Inner } 
case class X[S <: Inner](x: S) extends Base { final type T = S } 
case class Y[S <: Inner](y: S) extends Base { final type T = S } 

object Test extends App { 
    val encodedInner = Encoder[Inner].apply(I1(1)) 
    val decodedInner = Decoder[Inner].apply(encodedInner.hcursor) // Ok 
    println(decodedInner) // Right(I1(1)) 

    // Doesn't work: could not find implicit for Encoder[Base] etc 
    // val encoded = Encoder[Base].apply(Y(I1(1))) 
    // val decoded = Decoder[Base].apply(encoded.hcursor) 
    // println(decoded) 
} 

वहाँ एक रास्ता मैं प्राप्त कर सकते हैं जो मैं चाहता है? यदि नहीं, तो कुछ ऐसा करने के लिए मैं क्या बदल सकता हूं?

+0

यदि आपने ऑक्स पैटर्न के साथ प्रयास किया तो क्या होगा? जैसे 'ऑक्स टाइप करें [ए <: इनपुट] = बेस {टाइप टी = ए}' फिर 'ऑक्स' से बढ़ाएं? साथ ही, क्या आपको वास्तव में एक प्रकार का सदस्य होने की आवश्यकता है? – pyrospade

+0

वास्तव में, ऐसा लगता है कि आपकी केस कक्षाएं 'एस': इनर' की बजाय उनके तर्क के रूप में 'इनर' ले सकती हैं। – pyrospade

+0

मैंने एक उत्तर जोड़ा, लेकिन बाद में इसे और विस्तार और स्पष्टीकरण जोड़ने के साथ-साथ बेहतर कार्यान्वयन के लिए संशोधित किया है। – pyrospade

उत्तर

1

मुख्य कारण यह काम नहीं करता है क्योंकि आप कोशिश कर रहे हैं अनिवार्य रूप से कह T है कि किस प्रकार बिना

Encoder[Base { type T }] 

करना है। यह इस फ़ंक्शन को संकलित करने की अपेक्षा करने के समान है -

def foo[A] = implicitly[Encoder[List[A]]] 

आपको अपने प्रकार को स्पष्ट रूप से परिष्कृत करने की आवश्यकता है।

इस पर पहुंचने का एक तरीका Aux पैटर्न के साथ है। आप सामान्य type Aux[S] = Base { type T = S } का उपयोग नहीं कर सकते हैं क्योंकि यह उदाहरण प्राप्त करने का प्रयास करते समय आपको उत्पाद नहीं देगा (X और Y कक्षाएं एक प्रकार के उपनाम से विस्तार नहीं कर सकती हैं)। इसके बजाए, हम Aux के रूप में एक और मुहरबंद विशेषता बनाकर इसके आसपास हैक कर सकते हैं और हमारे केस क्लास उस से विस्तार कर सकते हैं।

इतने लंबे समय के अपने मामले कक्षाओं के सभी Base से Base.Aux के बजाय सीधे से विस्तार के रूप में, आप जिसके बाद हनन .asInstanceOf प्रकार प्रणाली को खुश करने के लिए उपयोग कर सकते हैं।

sealed trait Inner 
case class I1(i: Int) extends Inner 
case class I2(s: String) extends Inner 

sealed trait Base { type T <: Inner } 
object Base { 
    sealed trait Aux[S <: Inner] extends Base { type T = S } 
    implicit val encoder: Encoder[Base] = { 
    semiauto.deriveEncoder[Base.Aux[Inner]].asInstanceOf[Encoder[Base]] 
    } 
    implicit val decoder: Decoder[Base] = { 
    semiauto.deriveDecoder[Base.Aux[Inner]].asInstanceOf[Decoder[Base]] 
    } 
} 

val encoded = Encoder[Base].apply(Y(I1(1))) 
val decoded = Decoder[Base].apply(encoded.hcursor) 

ध्यान दें कि इनमें से बहुत से इस बात पर निर्भर करता है कि आप वास्तव में अपने प्रकार का उपयोग कैसे कर रहे हैं। मैं कल्पना करता हूं कि आप सीधे Encoder[Base] पर कॉल करने पर भरोसा नहीं करेंगे और इसके बजाय import io.circe.syntax._ का उपयोग करेंगे और .asJson एक्सटेंशन विधि को कॉल करेंगे। उस स्थिति में, आप Encoder[Base.Aux[S]] इंस्टेंस पर भरोसा कर सकते हैं, जिसे एनकोडेड/डीकोड किए गए मान के आधार पर अनुमानित किया जाएगा। .asInstanceOf हैक का उपयोग किए बिना आपके उपयोग के मामले में निम्नलिखित की तरह कुछ हो सकता है।

implicit def encoder[S <: Inner : Encoder]: Encoder[Base.Aux[S]] = { 
    semiauto.deriveEncoder 
} 

फिर, यह सब इस बात पर निर्भर करता है कि आप उदाहरणों का उपयोग कैसे कर रहे हैं। मुझे संदेह है कि आपको वास्तव में Base में एक प्रकार के सदस्य की आवश्यकता है, यदि आप इसे सामान्य पैरामीटर में ले जाते हैं तो चीजें आसान होती हैं ताकि डेरिववर आपके लिए प्रोडक्ट को समझ सके।

+0

प्रकार का सदस्य उस कारण के लिए है जो दायरे से बाहर है, इसे निश्चित रूप से हटाया जा सकता है लेकिन यह हर जगह कई कैस्केडिंग प्रकार पैरामीटर के साथ वाक्यविन्यास अधिभारित करेगा। यह निश्चित रूप से अलग किया जा सकता है, लेकिन यह मुझे लगता है कि बिंदु नहीं है। –

+0

इसके अलावा, आप बेस.एक्स से कैसे विस्तार करेंगे जहां ऑक्स साथी का एक प्रकार का सदस्य है? ऑक्स सिर्फ एक प्रकार का उपनाम है, आप इसे विस्तारित नहीं कर सकते हैं, मुझे –

+0

में अंतर दिखाई नहीं देता है लेकिन उपर्युक्त कोड में, बेस.एक्स एक विशेषता है, एक प्रकार का उपनाम नहीं है ... – Blaisorblade

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