2013-08-16 4 views
6

टाइप क्लास पैटर्न का उपयोग करने वाली स्कैला प्रोजेक्ट पर काम करते समय, मैं इस बात को लेकर गंभीर समस्या में प्रतीत होता हूं कि भाषा पैटर्न को कैसे लागू करती है: चूंकि स्कैला प्रकार-श्रेणी कार्यान्वयन का प्रबंधन किया जाना चाहिए प्रोग्रामर और भाषा नहीं, किसी भी प्रकार के वर्ग से संबंधित किसी भी चर को कभी भी माता-पिता के रूप में एनोटेटेड नहीं किया जा सकता है जब तक कि इसके प्रकार-वर्ग कार्यान्वयन को इसके साथ नहीं लिया जाता है।स्कैला प्रकार कक्षाओं को सामान्यीकृत करने में समस्याएं

इस बिंदु को वर्णन करने के लिए, मैं एक त्वरित उदाहरण कार्यक्रम कोडित गए हैं। कल्पना कीजिए कि आप ऐसे कार्यक्रम को लिखने की कोशिश कर रहे थे जो एक कंपनी के लिए विभिन्न प्रकार के कर्मचारियों को संभाल सके और उनकी प्रगति पर रिपोर्ट प्रिंट कर सके। स्काला में टाइप श्रेणी के पैटर्न के साथ इस समस्या के समाधान के लिए, आप कुछ इस तरह की कोशिश कर सकते: कर्मचारियों की

abstract class Employee 
class Packer(boxesPacked: Int, cratesPacked: Int) extends Employee 
class Shipper(trucksShipped: Int) extends Employee 

एक वर्ग पदानुक्रम मॉडलिंग विभिन्न प्रकार के काफी सरल। अब हम ReportMaker टाइप-क्लास को कार्यान्वित करते हैं।

trait ReportMaker[T] { 
    def printReport(t: T): Unit 
} 

implicit object PackerReportMaker extends ReportMaker[Packer] { 
    def printReport(p: Packer) { println(p.boxesPacked + p.cratesPacked) } 
} 

implicit object ShipperReportMaker extends ReportMaker[Shipper] { 
    def printReport(s: Shipper) { println(s.trucksShipped) } 
} 

सब कुछ ठीक है और अच्छा है कि, और अब हम है कि इस प्रकार दिखाई देंगे रोस्टर वर्ग के कुछ प्रकार लिख सकते हैं:

class Roster { 
    private var employees: List[Employee] = List() 

    def reportAndAdd[T <: Employee](e: T)(implicit rm: ReportMaker[T]) { 
     rm.printReport(e) 
     employees = employees :+ e 
    } 
} 

तो यह काम करता है। अब, हमारे टाइप-क्लास के लिए धन्यवाद, हम रिपोर्ट में एक पैकर या शिपर ऑब्जेक्ट पास कर सकते हैं और विधि जोड़ें, और यह रिपोर्ट प्रिंट करेगा और कर्मचारी को रोस्टर में जोड़ देगा। हालांकि, रोस्टर में प्रत्येक कर्मचारी की रिपोर्ट मुद्रित करने का प्रयास करने वाली एक विधि लिखना असंभव होगा, रिपोर्ट के लिए पारित आरएम ऑब्जेक्ट को स्पष्ट रूप से संग्रहीत किए बिना और जोड़ें!

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

(defprotocol Reporter 
     (report [this] "Produce a string report of the object.")) 

    (defrecord Packer [boxes-packed crates-packed] 
     Reporter 
     (report [this] (str (+ (:boxes-packed this) (:crates-packed this))))) 
    (defrecord Shipper [trucks-shipped] 
     Reporter 
     (report [this] (str (:trucks-shipped this)))) 

    (defn report-roster [roster] 
     (dorun (map #(println (report %)) roster))) 

    (def steve (Packer. 10 5)) 
    (def billy (Shipper. 5)) 

    (def roster [steve billy]) 

    (report-roster roster) 

अलावा प्रकार सूची में कर्मचारी सूची मोड़ के बजाय बुरा समाधान से [(कर्मचारी, ReportMaker [कर्मचारी]), स्काला इस समस्या को हल करने के लिए किसी भी तरह से की पेशकश करता है? और यदि नहीं, क्योंकि स्कैला पुस्तकालय टाइप-क्लासेस का व्यापक उपयोग करते हैं, इसलिए इसे क्यों संबोधित नहीं किया गया है?

+2

यह अभी तक subtyping सब कुछ खिलवाड़ का एक और उदाहरण है। यदि आप उप-प्रकार के बजाए अपने कर्मचारी उप-वर्गों को रचनाकारों (हास्केल अर्थ में) के रूप में सोचते हैं, तो आप पाएंगे कि टाइप क्लास दृष्टिकोण अधिक आरामदायक है। –

+3

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

उत्तर

5

जिस तरह से आप आम तौर पर स्काला में एक बीजीय डेटा प्रकार लागू होता case वर्गों के साथ होगा:

sealed trait Employee 
case class Packer(boxesPacked: Int, cratesPacked: Int) extends Employee 
case class Shipper(trucksShipped: Int) extends Employee 

यह Packer और Shipper निर्माताओं के लिए पैटर्न एक्सट्रैक्टर्स देता है ताकि आप उन पर मिलान कर सकते हैं।

दुर्भाग्यवश, Packer और Shipper भी विशिष्ट (उप) प्रकार हैं, लेकिन स्कैला में बीजगणित डेटा प्रकार को एन्कोड करने के पैटर्न का हिस्सा इसे अनदेखा करने के बारे में अनुशासित होना है। इसके बजाय, जब एक पैकर या shipper के बीच भेद, पैटर्न मिलान का उपयोग के रूप में आप हास्केल में होगा:

implicit object EmployeeReportMaker extends ReportMaker[Employee] { 
    def printReport(e: Employee) = e match { 
    case Packer(boxes, crates) => // ... 
    case Shipper(trucks)  => // ... 
    } 
} 

आप कोई अन्य प्रकार जिसके लिए आप एक ReportMaker उदाहरण की जरूरत है, तो शायद प्रकार वर्ग की जरूरत नहीं है, और आप केवल printReport फ़ंक्शन का उपयोग कर सकते हैं।

+0

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

0

हालांकि, एक विधि है कि रोस्टर में हर कर्मचारी की रिपोर्ट बाहर मुद्रित करने के लिए असंभव हो जाएगा स्पष्ट rm वस्तु reportAndAdd के लिए पारित हो जाता है कि भंडारण के बिना, प्रयास लेखन!

आपकी सटीक समस्या का निश्चित नहीं है। निम्नलिखित (आई/ओ उत्पादन बिंदु पर concatenated अलग रिपोर्ट के साथ स्पष्ट रूप से) काम करना चाहिए:

def printReport(ls: List[Employee]) = { 
    def printReport[T <: Employee](e: T)(implicit rm: ReportMaker[T]) = rm.printReport(e) 
    ls foreach(printReport(_)) 
} 

हालांकि, मैं कर रहा/ओ के खिलाफ है कहीं नीचे विधि-कॉल-वृक्ष (या तरीकों iteratively बुलाया में) 'कार्यात्मक दर्शन'। स्ट्रिंग/सूची [स्ट्रिंग]/अन्य सटीक संरचना के रूप में व्यक्तिगत उप-रिपोर्ट जेनरेट करने के लिए बेहतर, उन्हें सभी बाहरी विधि तक बबल करें और I/O को एक ही हिट में करें। उदा .:

trait ReportMaker[T] { 
    def generateReport(t: T): String 
} 

(डालने निहित वस्तुओं क्यू के समान ...)

def printReport(ls: List[Employee]) = { 
    def generateReport[T <: Employee](e: T)(implicit rm: ReportMaker[T]): String = rm.generateReport(e) 
    // trivial example with string concatenation - but could do any fancy combine :) 
    someIOManager.print(ls.map(generateReport(_)).mkString("""\n"""))) 
} 
संबंधित मुद्दे