2016-01-19 7 views
7

में केस क्लास द्वारा विस्तारित करने के लिए एक विशेषता को परिभाषित करें मेरे पास कुछ केस क्लास हैं जिनकी विधि tupled है जो इसके साथी ऑब्जेक्ट में परिभाषित है। जैसा कि इसे साथी वस्तुओं में नीचे दिए गए कोड से देखा जा सकता है, यह केवल कोड डुप्लिकेशन है।स्केल

case class Book(id: Int, isbn: String, name: String) 

object Book { 
    def tupled = (Book.apply _).tupled // Duplication 
} 


case class Author(id: Int, name: String) 

object Author { 
    def tupled = (Author.apply _).tupled // Duplication 
} 

एक और सवाल (can a scala self type enforce a case class type) से ऐसा प्रतीत होता है कि हम एक विशेषता का आत्म प्रकार लागू नहीं कर सकते एक मामले वर्ग होने के लिए।

क्या कोई विशेषता परिभाषित करने का कोई तरीका है (Tupled कहें) जिसे निम्नलिखित के रूप में लागू किया जा सकता है?

// What would be value of ??? 
trait Tupled { 
    self: ??? => 

    def tupled = (self.apply _).tupled 
} 

// Such that I can replace tupled definition with Trait 
object Book extends Tupled { 
} 

उत्तर

12

वहाँ स्काला में FunctionN प्रकार के बीच कोई रिश्ता नहीं है, इसलिए यह संभव नहीं बिना arity स्तरीय बॉयलरप्लेट कहीं-वहाँ बस कोई रास्ता नहीं साथी से अधिक सार करने के लिए वस्तुओं 'apply तरीकों यह करने के लिए के सभी संभव संख्या की गणना के बिना है सदस्य हैं।

आप इसे CompanionN[A, B, C, ...] लक्षणों के समूह के साथ हाथ से कर सकते हैं, लेकिन यह बहुत परेशान है।

import shapeless.{ Generic, HList }, shapeless.ops.product.ToHList 

class CaseClassCompanion[C] { 
    def tupled[P <: Product, R <: HList](p: P)(implicit 
    gen: Generic.Aux[C, R], 
    toR: ToHList.Aux[P, R] 
): C = gen.from(toR(p)) 
} 

और फिर:

case class Book(id: Int, isbn: String, name: String) 
object Book extends CaseClassCompanion[Book] 

case class Author(id: Int, name: String) 
object Author extends CaseClassCompanion[Author] 

जो तुम इस तरह उपयोग कर सकते हैं:

scala> Book.tupled((0, "some ISBN", "some name")) 
res0: Book = Book(0,some ISBN,some name) 

scala> Author.tupled((0, "some name")) 
res1: Author = Author(0,some name) 

आप हो सकता है Shapeless एक बेहतर समाधान है, जो आप की तरह निम्नलिखित कुछ लिखने के लिए अनुमति देता है प्रदान करता है CaseClassCompanion भाग भी नहीं चाहते हैं, क्योंकि एक सामान्य विधि बनाना संभव है जो टुपल्स को केस क्लास में परिवर्तित करता है (सदस्य प्रकार लाइन अप मानते हैं):

class PartiallyAppliedProductToCc[C] { 
    def apply[P <: Product, R <: HList](p: P)(implicit 
    gen: Generic.Aux[C, R], 
    toR: ToHList.Aux[P, R] 
): C = gen.from(toR(p)) 
} 

def productToCc[C]: PartiallyAppliedProductToCc[C] = 
    new PartiallyAppliedProductToCc[C] 

और फिर:

scala> productToCc[Book]((0, "some ISBN", "some name")) 
res2: Book = Book(0,some ISBN,some name) 

scala> productToCc[Author]((0, "some name")) 
res3: Author = Author(0,some name) 

यह साथी वस्तु पर apply विधि के बाद से अप करने के लिए 22 सदस्यों (के मामले में कक्षाओं के लिए काम करेंगे एक समारोह को ईटा-विस्तार नहीं किया जा सकता है अगर 22 से अधिक तर्क)।

+2

एक साइड नोट के रूप में, 'सेप्पल' इस विधि के लिए एक बेहतर नाम हो सकता है-यह सच है कि आप 'लागू' को जोड़ रहे हैं, लेकिन परिणाम 'tupled' बनाता है, रूपांतरण की तरह ध्वनि है _to_ इसके बजाय एक tuple है अभी से_। –

+0

बहुत बहुत धन्यवाद !!! – TheKojuEffect

+0

@TravisBrown मैं 1 पैरामीटर के वर्ग वर्गों के साथ एक ही प्रभाव कैसे प्राप्त कर सकता हूं? – vicaba

2

समस्या यह है कि apply का हस्ताक्षर मामले से अलग है, और इन कार्यों के लिए कोई आम विशेषता नहीं है। Book.tupled और Author.tupled मूल रूप से एक ही कोड है, लेकिन बहुत अलग हस्ताक्षर हैं। इसलिए, समाधान उतना अच्छा नहीं हो सकता जितना हम चाहते हैं।


मैं बॉयलरप्लेट को काटने के लिए एनोटेशन मैक्रो का उपयोग करके एक तरीके से गर्भ धारण कर सकता हूं। चूंकि मानक लाइब्रेरी के साथ ऐसा करने का कोई अच्छा तरीका नहीं है, इसलिए मैं कोड जनरेशन का सहारा लेगा (जिसमें अभी भी संकलन-समय सुरक्षा है)। यहां चेतावनी यह है कि एनोटेशन मैक्रोज़ को macro paradise कंपाइलर प्लगइन के उपयोग की आवश्यकता होती है। मैक्रोज़ को एक अलग संकलन इकाई में भी होना चाहिए (जैसे किसी अन्य एसबीटी उप-प्रोजेक्ट)। कोड जो एनोटेशन का उपयोग करता है उसे मैक्रो पैराडाइज प्लगइन के उपयोग की भी आवश्यकता होगी।

import scala.annotation.{ StaticAnnotation, compileTimeOnly } 
import scala.language.experimental.macros 
import scala.reflect.macros.whitebox.Context 

@compileTimeOnly("enable macro paradise to expand macro annotations") 
class Tupled extends StaticAnnotation { 
    def macroTransform(annottees: Any*): Any = macro tupledMacroImpl.impl 
} 

object tupledMacroImpl { 

    def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { 
    import c.universe._ 
    val result = annottees map (_.tree) match { 
     // A case class with companion object, we insert the `tupled` method into the object 
     // and leave the case class alone. 
     case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }") 
     :: (objDef @ q"object $objName extends { ..$objEarlyDefs } with ..$objParents { $objSelf => ..$objDefs }") 
     :: Nil if mods.hasFlag(Flag.CASE) => 
     q""" 
      $classDef 
      object $objName extends { ..$objEarlyDefs } with ..$objParents { $objSelf => 
      ..$objDefs 
      def tupled = ($objName.apply _).tupled 
      } 
     """ 
     case _ => c.abort(c.enclosingPosition, "Invalid annotation target: must be a companion object of a case class.") 
    } 

    c.Expr[Any](result) 
    } 

} 

उपयोग:

@Tupled 
case class Author(id: Int, name: String) 

object Author 


// Exiting paste mode, now interpreting. 

defined class Author 
defined object Author 

scala> Author.tupled 
res0: ((Int, String)) => Author = <function1> 

वैकल्पिक रूप से, कुछ इस तरह निराकार के साथ संभव हो सकता है। @ ट्रेविसब्राउन का बेहतर उत्तर देखें।

+0

आपके उत्तर के लिए धन्यवाद। – TheKojuEffect