निहित रूपांतरण बनाम प्रकार वर्ग


93

स्काला में, हम मौजूदा या नए प्रकारों को वापस करने के लिए कम से कम दो तरीकों का उपयोग कर सकते हैं। मान लीजिए कि हम यह व्यक्त करना चाहते हैं कि कुछ का उपयोग करके मात्रा निर्धारित की जा सकती है Int। हम निम्नलिखित विशेषता को परिभाषित कर सकते हैं।

निष्प्राण रूपांतरण

trait Quantifiable{ def quantify: Int }

और फिर हम उदाहरणार्थ स्ट्रिंग्स और सूचियों को निर्धारित करने के लिए अंतर्निहित रूपांतरण का उपयोग कर सकते हैं।

implicit def string2quant(s: String) = new Quantifiable{ 
  def quantify = s.size 
}
implicit def list2quantifiable[A](l: List[A]) = new Quantifiable{ 
  val quantify = l.size 
}

इन्हें आयात करने के बाद, हम quantifyस्ट्रिंग्स और सूचियों पर विधि को कॉल कर सकते हैं । ध्यान दें कि मात्रात्मक सूची इसकी लंबाई को संग्रहीत करती है, इसलिए यह बाद के कॉल पर सूची के महंगे ट्रैवर्स से बचा जाता है quantify

कक्षाएं टाइप करें

एक विकल्प एक "गवाह" को परिभाषित करना है Quantified[A]जो बताता है कि कुछ प्रकार की Aमात्रा निर्धारित की जा सकती है।

trait Quantified[A] { def quantify(a: A): Int }

हम तब Stringऔर Listकहीं के लिए इस प्रकार के वर्ग के उदाहरण देते हैं ।

implicit val stringQuantifiable = new Quantified[String] {
  def quantify(s: String) = s.size 
}

और अगर हम तब एक विधि लिखते हैं जिसे अपने तर्कों को निर्धारित करने की आवश्यकता होती है, हम लिखते हैं:

def sumQuantities[A](as: List[A])(implicit ev: Quantified[A]) = 
  as.map(ev.quantify).sum

या संदर्भ बाध्य सिंटैक्स का उपयोग कर:

def sumQuantities[A: Quantified](as: List[A]) = 
  as.map(implicitly[Quantified[A]].quantify).sum

लेकिन कब किस विधि का उपयोग करें?

अब सवाल आता है। मैं उन दो अवधारणाओं के बीच कैसे निर्णय ले सकता हूं?

मैंने अब तक क्या देखा है।

कक्षाएं टाइप करें

  • टाइप कक्षाएं अच्छा संदर्भ बाध्य वाक्यविन्यास की अनुमति देती हैं
  • प्रकार कक्षाओं के साथ मैं प्रत्येक उपयोग पर एक नया आवरण वस्तु नहीं बनाता
  • यदि प्रकार वर्ग में कई प्रकार के पैरामीटर हैं, तो संदर्भ बाध्य सिंटैक्स अब काम नहीं करता है; कल्पना कीजिए कि मैं न केवल पूर्णांकों के साथ बल्कि कुछ सामान्य प्रकार के मूल्यों के साथ चीजों को मात्रा देना चाहता हूं T। मैं एक टाइप क्लास बनाना चाहूंगाQuantified[A,T]

निहितार्थ

  • जब से मैं एक नई वस्तु बनाता हूं, मैं वहां मूल्यों को कैश कर सकता हूं या बेहतर प्रतिनिधित्व कर सकता हूं; लेकिन मुझे इससे बचना चाहिए, क्योंकि यह कई बार हो सकता है और एक स्पष्ट रूपांतरण शायद केवल एक बार ही होगा?

मुझे जवाब से क्या उम्मीद है

वर्तमान एक (या अधिक) केस केस (एस) का उपयोग करें, जहां दोनों अवधारणाओं के बीच अंतर मायने रखता है और समझाता है कि मैं दूसरे पर क्यों पसंद करूंगा। साथ ही दो अवधारणाओं का सार और एक दूसरे से उनके संबंध को स्पष्ट करना, उदाहरण के बिना भी अच्छा होगा।


प्रकार वर्ग बिंदुओं में कुछ भ्रम है जहां आप "बाउंड बाउंड" का उल्लेख करते हैं, हालांकि प्रकार वर्ग संदर्भ सीमा का उपयोग करते हैं।
डैनियल सी। सोबरल

1
+1 उत्कृष्ट प्रश्न; मैं इसका पूरी तरह से जवाब देने में दिलचस्पी रखता हूं।
दान बर्टन

@ डैनियल धन्यवाद। मुझे हमेशा वो गलत मिलते हैं।
जिग्गीस्टार

2
आप एक ही स्थान में गलत कर रहे हैं: अपने दूसरे अंतर्निहित रूपांतरण उदाहरण में संग्रहीत की गई sizeएक मूल्य की किसी सूची की और कहते हैं कि इसका हिसाब लगाना आगामी कॉल पर सूची की महंगी ट्रेवर्सल से बचा जाता है, लेकिन करने के लिए अपने हर कॉल पर ट्रिगर किया जाता है इस तरह से फिर से संपत्ति की भरपाई और पुनर्मूल्यांकन किया जा रहा है। मैं जो कह रहा हूं वह यह है कि वास्तव में निहित रूपांतरण के साथ परिणामों को कैश करने का कोई तरीका नहीं है। quantifylist2quantifiableQuantifiablequantify
निकिता वोल्कोव

@NikitaVolkov आपका अवलोकन सही है। और मैं दूसरे से अंतिम पैराग्राफ में अपने प्रश्न में इसे स्वीकार करता हूं। कैशिंग काम करता है, जब एक परिवर्तित विधि कॉल के बाद परिवर्तित ऑब्जेक्ट लंबे समय तक उपयोग किया जाता है (और शायद इसके परिवर्तित रूप में पारित हो जाता है)। जबकि प्रकार की कक्षाएं संभवत: गहरे जाने पर अनकवर्ड ऑब्जेक्ट के साथ जंजीर में जकड़ जाएंगी।
जिगसिस्टार

जवाबों:


42

हालांकि मैं स्काला इन डेप्थ से अपनी सामग्री को डुप्लिकेट नहीं करना चाहता , लेकिन मुझे लगता है कि यह ध्यान देने योग्य है कि प्रकार के वर्ग / प्रकार के लक्षण असीम रूप से अधिक लचीले हैं।

def foo[T: TypeClass](t: T) = ...

एक डिफ़ॉल्ट प्रकार वर्ग के लिए अपने स्थानीय वातावरण को खोजने की क्षमता है। हालाँकि, मैं दो तरीकों में से किसी भी समय डिफ़ॉल्ट व्यवहार को ओवरराइड कर सकता हूं:

  1. शॉर्ट सर्किट निहित लुकअप में स्कोप में एक अंतर्निहित प्रकार वर्ग उदाहरण बनाना / आयात करना
  2. सीधे एक प्रकार की कक्षा पास करना

यहाँ एक उदाहरण है:

def myMethod(): Unit = {
   // overrides default implicit for Int
   implicit object MyIntFoo extends Foo[Int] { ... }
   foo(5)
   foo(6) // These all use my overridden type class
   foo(7)(new Foo[Int] { ... }) // This one needs a different configuration
}

यह प्रकार की कक्षाओं को असीम रूप से अधिक लचीला बनाता है। एक और बात यह है कि प्रकार वर्ग / लक्षण बेहतर लुकअप का समर्थन करते हैं ।

अपने पहले उदाहरण में, यदि आप एक अंतर्निहित दृश्य का उपयोग करते हैं, तो संकलक इसके लिए एक अंतर्निहित लुकअप करेगा:

Function1[Int, ?]

जो Function1साथी की वस्तु और साथी वस्तु को देखेगा Int

सूचना है कि Quantifiableहै कहीं नहीं अंतर्निहित देखने में। इसका मतलब है कि आपको किसी पैकेज ऑब्जेक्ट में निहित दृश्य रखना होगा या इसे दायरे में आयात करना होगा। यह याद रखना कि क्या चल रहा है, यह अधिक काम है।

दूसरी ओर, एक प्रकार वर्ग स्पष्ट है । आप देखते हैं कि यह विधि हस्ताक्षर में क्या देख रहा है। आपके पास एक अंतर्निहित लुकअप भी है

Quantifiable[Int]

जो Quantifiableसाथी की वस्तु और साथी की वस्तु में दिखेगा Int। मतलब यह है कि आप डिफॉल्ट प्रदान कर सकते हैं और नए प्रकार (एक MyStringवर्ग की तरह ) अपने साथी ऑब्जेक्ट में एक डिफ़ॉल्ट प्रदान कर सकते हैं और इसे खोजा जाएगा।

सामान्य तौर पर, मैं प्रकार की कक्षाओं का उपयोग करता हूं। वे प्रारंभिक उदाहरण के लिए असीम रूप से अधिक लचीले हैं। एकमात्र स्थान जिसका मैं अंतर्निहित रूपांतरण का उपयोग करता हूं, जब एक स्केल आवरण और जावा लाइब्रेरी के बीच एपीआई परत का उपयोग किया जाता है, और यहां तक ​​कि यह 'खतरनाक' हो सकता है यदि आप सावधान नहीं हैं।


20

एक मानदंड जो खेल में आ सकता है वह यह है कि आप नई सुविधा को किस तरह "महसूस" करना चाहते हैं; निहित रूपांतरणों का उपयोग करते हुए, आप यह देख सकते हैं कि यह एक और तरीका है:

"my string".newFeature

... प्रकार की कक्षाओं का उपयोग करते समय यह हमेशा ऐसा लगेगा जैसे आप एक बाहरी फ़ंक्शन को बुला रहे हैं:

newFeature("my string")

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

trait Default[T] { def value : T }

implicit object DefaultInt extends Default[Int] {
  def value = 42
}

implicit def listsHaveDefault[T : Default] = new Default[List[T]] {
  def value = implicitly[Default[T]].value :: Nil
}

def default[T : Default] = implicitly[Default[T]].value

scala> default[List[List[Int]]]
resN: List[List[Int]] = List(List(42))

इस उदाहरण से यह भी पता चलता है कि अवधारणाएँ किस तरह से संबंधित हैं: प्रकार की कक्षाएं लगभग उतनी उपयोगी नहीं होंगी यदि उनके कई उदाहरणों का असीम उत्पादन करने के लिए कोई तंत्र नहीं था; implicitविधि के बिना (रूपांतरण नहीं, माना जाता है), मैं केवल बहुत ही प्रकार की Defaultसंपत्ति रख सकता था ।


@Phillippe - आपके द्वारा लिखी गई तकनीक में मेरी बहुत दिलचस्पी है ... लेकिन ऐसा लगता है कि स्कैला 2.11.6 पर काम नहीं करती। मैंने एक प्रश्न पोस्ट किया है जो आपके उत्तर के बारे में अपडेट देगा। अग्रिम धन्यवाद अगर आप मदद कर सकते हैं: कृपया देखें: stackoverflow.com/questions/31910923/…
क्रिस बेडफोर्ड

@ क्रिसहेडफोर्ड I ने defaultभविष्य के पाठकों के लिए परिभाषा जोड़ी ।
फिलिप

13

आप केवल नामांकित आवरण के साथ फ़ंक्शन अनुप्रयोग के लिए दो तकनीकों के बीच अंतर के बारे में सोच सकते हैं। उदाहरण के लिए:

trait Foo1[A] { def foo(a: A): Int }  // analogous to A => Int
trait Foo0    { def foo: Int }        // analogous to Int

पूर्व का एक उदाहरण प्रकार के एक कार्य को अंजाम देता है A => Int, जबकि बाद का एक उदाहरण पहले से ही एक पर लागू किया गया है A। आप पैटर्न जारी रख सकते हैं ...

trait Foo2[A, B] { def foo(a: A, b: B): Int } // sort of like A => B => Int

इस प्रकार आप कुछ उदाहरण Foo1[B]के आंशिक अनुप्रयोग की तरह के बारे में सोच सकते हैं । इसका एक बड़ा उदाहरण मीलों सबिन ने "स्काला में कार्यात्मक निर्भरता" के रूप में लिखा था ।Foo2[A, B]A

तो वास्तव में मेरी बात यह है कि, सिद्धांत रूप में:

  • "पिंपिंग" एक वर्ग (अंतर्निहित रूपांतरण के माध्यम से) "शून्य क्रम" केस है ...
  • एक टाइपकास्ट घोषित करना "पहला आदेश" मामला है ...
  • बहु-पैरामीटर टाइपकेपल्स फंडेफ़्स (या फंडपेप्स जैसी चीज़) के साथ सामान्य मामला है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.