स्काल इंप्लिकेशन्स के लिए कहाँ दिखता है?


398

स्काला में नए लोगों के लिए एक अंतर्निहित प्रश्न यह प्रतीत होता है: संकलक को इंसिप्लिट्स कहाँ दिखता है? मेरा तात्पर्य यह है कि प्रश्न कभी पूरी तरह से नहीं लगता है, जैसे कि इसके लिए शब्द नहीं थे। :-) उदाहरण के लिए, integralनीचे के लिए मान कहाँ से आते हैं?

scala> import scala.math._
import scala.math._

scala> def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit

scala> foo(0)
scala.math.Numeric$IntIsIntegral$@3dbea611

scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@48c610af

एक और सवाल जो उन लोगों का पालन करता है जो पहले सवाल का जवाब जानने का फैसला करते हैं कि संकलक स्पष्ट अस्पष्टता की कुछ स्थितियों में (जो कि वैसे भी संकलन करता है) का उपयोग करने के लिए निहितार्थ का चयन कैसे करता है?

उदाहरण के लिए, scala.Predefदो रूपांतरणों को परिभाषित करता है String: एक को WrappedStringऔर दूसरे को StringOps। दोनों वर्ग, हालांकि, बहुत सारे तरीके साझा करते हैं, इसलिए स्काला को कॉल करते समय अस्पष्टता की शिकायत क्यों नहीं होती है map?

नोट: इस सवाल से प्रेरित था यह अन्य प्रश्न एक अधिक सामान्य ढंग से समस्या बताते हुए की उम्मीद में,। उदाहरण वहां से कॉपी किया गया था, क्योंकि यह उत्तर में संदर्भित है।

जवाबों:


554

इंप्लिकिट्स के प्रकार

स्काला में इंप्लिसिट्स या तो एक मान को संदर्भित करता है जिसे "स्वचालित रूप से" पारित किया जा सकता है, इसलिए बोलने के लिए, या एक प्रकार से दूसरे में रूपांतरण जो स्वचालित रूप से बनाया गया है।

अव्यवस्थित रूपांतरण

, उत्तरार्द्ध प्रकार के बारे में बहुत संक्षेप में बात हो रही है अगर एक एक विधि कॉल mएक वस्तु पर oएक वर्ग के Cहैं, और उस वर्ग नहीं है समर्थन विधि m, तो स्काला से एक अंतर्निहित रूपांतरण के लिए दिखेगा Cकुछ ऐसा है जो करने के लिए करता है समर्थन m। एक सरल उदाहरण इस पद्धति mapपर होगा String:

"abc".map(_.toInt)

Stringविधि का समर्थन नहीं करता mapहै, लेकिन StringOpsकरता है, और वहाँ से एक अंतर्निहित रूपांतरण है Stringकरने के लिए StringOpsउपलब्ध है (देखें implicit def augmentStringपर Predef)।

निहित पैरामीटर

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

इस मामले में, किसी को निहित की आवश्यकता की घोषणा करनी होगी, जैसे कि fooविधि घोषणा:

def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}

सीमा देखें

एक स्थिति है जहां एक निहितार्थ एक अंतर्निहित रूपांतरण और एक अंतर्निहित पैरामीटर दोनों है। उदाहरण के लिए:

def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)

getIndex("abc", 'a')

विधि getIndexकिसी भी वस्तु को प्राप्त कर सकती है, जब तक कि उसके वर्ग से एक अंतर्निहित रूपांतरण उपलब्ध है Seq[T]। इस कारण से, मैं एक पारित कर सकते हैं Stringकरने के लिए getIndexहै, और यह काम करेंगे।

दृश्यों के पीछे, संकलक में परिवर्तन seq.IndexOf(value)होता है conv(seq).indexOf(value)

यह इतना उपयोगी है कि उन्हें लिखने के लिए वाक्य रचना चीनी है। इस प्रकार से बनाई जाने वाली चीनी का उपयोग करके इसे इस getIndexतरह परिभाषित किया जा सकता है:

def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)

इस सिनटैक्ट शुगर को व्यू बाउंड , एक अपर बाउंड ( CC <: Seq[Int]) या लोअर बाउंड ( T >: Null) के समान बताया गया है।

प्रसंग सीमा

निहित मापदंडों में एक और सामान्य पैटर्न प्रकार वर्ग पैटर्न है । यह पैटर्न उन वर्गों के लिए सामान्य इंटरफेस के प्रावधान को सक्षम करता है जो उन्हें घोषित नहीं करते थे। यह दोनों एक पुल पैटर्न के रूप में सेवा कर सकता है - चिंताओं को अलग करना - और एक एडाप्टर पैटर्न के रूप में।

जिस Integralकक्षा का आपने उल्लेख किया है वह टाइप क्लास पैटर्न का एक उत्कृष्ट उदाहरण है। स्काला के मानक पुस्तकालय पर एक और उदाहरण है Ordering। एक पुस्तकालय है जो इस पैटर्न का भारी उपयोग करता है, जिसे स्कलाज़ कहा जाता है।

यह इसके उपयोग का एक उदाहरण है:

def sum[T](list: List[T])(implicit integral: Integral[T]): T = {
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}

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

def sum[T : Integral](list: List[T]): T = {
    val integral = implicitly[Integral[T]]
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}

संदर्भ सीमाएं तब अधिक उपयोगी होती हैं जब आपको उन्हें अन्य तरीकों से पास करने की आवश्यकता होती है जो उनका उपयोग करते हैं। उदाहरण के लिए, विधि sortedपर Seqएक निहित की जरूरत है Ordering। एक विधि बनाने के लिए reverseSort, कोई लिख सकता है:

def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse

क्योंकि Ordering[T]अंतर्निहित रूप से reverseSortइसे पारित किया गया था , इसलिए यह इसे अंतर्निहित रूप से पारित कर सकता है sorted

इंप्लिकिट कहाँ से आते हैं?

जब कंपाइलर एक निहित की आवश्यकता को देखता है, या तो क्योंकि आप एक ऐसी विधि को बुला रहे हैं जो ऑब्जेक्ट की कक्षा में मौजूद नहीं है, या क्योंकि आप एक ऐसी विधि को बुला रहे हैं जिसके लिए एक अंतर्निहित पैरामीटर की आवश्यकता होती है, यह एक अंतर्निहित की खोज करेगा जो आवश्यकता को फिट करेगा ।

यह खोज कुछ नियमों का पालन करती है जो परिभाषित करते हैं कि कौन से निहितार्थ दिखाई देते हैं और जो नहीं हैं। निम्न तालिका यह दिखाती है कि कंपाइलर इम्पीचिट्स की खोज करेगा, जोश सुरेथ द्वारा इम्पीकिट्स के बारे में एक उत्कृष्ट प्रस्तुति से लिया गया था , जिसे मैं दिल से किसी को भी अपने स्काला ज्ञान में सुधार करने की सलाह देता हूं। यह तब से पूरक है जब प्रतिक्रिया और अपडेट के साथ।

नीचे दिए गए नंबर 1 के तहत उपलब्ध निहितार्थों में संख्या 2 के तहत पूर्ववर्तीता है। इसके अलावा, यदि कई योग्य तर्क हैं जो अंतर्निहित पैरामीटर के प्रकार से मेल खाते हैं, तो एक सबसे विशिष्ट को स्थिर ओवरलोडिंग रिज़ॉल्यूशन के नियमों का उपयोग करके चुना जाएगा (देखें स्काला) विशिष्टता §6.26.3)। इस उत्तर के अंत में एक प्रश्न I लिंक से अधिक विस्तृत जानकारी मिल सकती है।

  1. पहले वर्तमान दायरे में देखें
    • वर्तमान स्कोप में परिभाषित इम्प्लाइक
    • आयात आयात करें
    • वाइल्डकार्ड आयात
    • अन्य फ़ाइलों में समान गुंजाइश
  2. अब इसमें जुड़े प्रकारों को देखें
    • एक प्रकार की साथी वस्तुएँ
    • किसी तर्क के प्रकार का अनुमानित दायरा (2.9.1)
    • प्रकार के तर्कों का निहित दायरा (2.8.0)
    • नेस्टेड प्रकारों के लिए बाहरी वस्तुएं
    • अन्य आयाम

आइए उनके लिए कुछ उदाहरण दें:

करंट स्कोप को वर्तमान स्कोप में परिभाषित किया गया

implicit val n: Int = 5
def add(x: Int)(implicit y: Int) = x + y
add(5) // takes n from the current scope

स्पष्ट आयात

import scala.collection.JavaConversions.mapAsScalaMap
def env = System.getenv() // Java map
val term = env("TERM")    // implicit conversion from Java Map to Scala Map

वाइल्डकार्ड आयात

def sum[T : Integral](list: List[T]): T = {
    val integral = implicitly[Integral[T]]
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}

अन्य फ़ाइलों में समान स्कोप

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

यह पहले उदाहरण की तरह है, लेकिन अनुमान लगाने की परिभाषा इसके उपयोग की तुलना में एक अलग फ़ाइल में है। यह भी देखें कि कैसे आरेख में लाने के लिए पैकेज ऑब्जेक्ट का उपयोग किया जा सकता है।

एक प्रकार की साथी वस्तुएँ

यहां नोट के दो ऑब्जेक्ट साथी हैं। सबसे पहले, "स्रोत" प्रकार के ऑब्जेक्ट साथी पर ध्यान दिया जाता है। उदाहरण के लिए, ऑब्जेक्ट के अंदर Optionएक अंतर्निहित रूपांतरण होता है Iterable, इसलिए कोई उन Iterableतरीकों को कॉल कर सकता है Option, या Optionकिसी से अपेक्षा कर सकता है Iterable। उदाहरण के लिए:

for {
    x <- List(1, 2, 3)
    y <- Some('x')
} yield (x, y)

उस अभिव्यक्ति को संकलक द्वारा अनुवादित किया जाता है

List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))

हालांकि, List.flatMapएक की उम्मीद है TraversableOnce, जो Optionनहीं है। संकलक तब Optionवस्तु के साथी को देखता है और रूपांतरण को पाता है Iterable, जो कि एक है TraversableOnce, जिससे यह अभिव्यक्ति सही हो जाती है।

दूसरा, अपेक्षित प्रकार के साथी वस्तु:

List(1, 2, 3).sorted

विधि sortedएक निहित लेता है Ordering। इस मामले में, यह ऑब्जेक्ट के अंदर दिखता है Ordering, वर्ग के साथी Ordering, और वहां एक निहित पाता है Ordering[Int]

ध्यान दें कि सुपर क्लास की साथी वस्तुओं को भी देखा जाता है। उदाहरण के लिए:

class A(val n: Int)
object A { 
    implicit def str(a: A) = "A: %d" format a.n
}
class B(val x: Int, y: Int) extends A(y)
val b = new B(5, 2)
val s: String = b  // s == "A: 2"

इस तरह से स्काला ने निहित Numeric[Int]और Numeric[Long]आपके प्रश्न में, वैसे पाया, जैसे वे अंदर पाए जाते हैं Numeric, नहीं Integral

एक तर्क के प्रकार का स्पष्ट स्कोप

यदि आपके पास एक तर्क प्रकार के साथ एक विधि है A, तो प्रकार के अंतर्निहित दायरे पर Aभी विचार किया जाएगा। "निहित गुंजाइश" से मेरा मतलब है कि इन सभी नियमों को पुनरावर्ती रूप से लागू किया जाएगा - उदाहरण के लिए, Aउपर्युक्त नियम के अनुसार , के साथी ऑब्जेक्ट को खोज के लिए खोजा जाएगा।

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

class A(val n: Int) {
  def +(other: A) = new A(n + other.n)
}
object A {
  implicit def fromInt(n: Int) = new A(n)
}

// This becomes possible:
1 + new A(1)
// because it is converted into this:
A.fromInt(1) + new A(1)

यह स्काला 2.9.1 के बाद से उपलब्ध है।

टाइप आर्ग्युमेंट्स का इंप्लांट स्कोप

टाइप क्लास पैटर्न को वास्तव में काम करने के लिए यह आवश्यक है। Orderingउदाहरण के लिए विचार करें : यह अपने साथी ऑब्जेक्ट में कुछ इम्पिटिट के साथ आता है, लेकिन आप इसमें सामान नहीं जोड़ सकते। तो आप Orderingअपने वर्ग के लिए कैसे बना सकते हैं जो स्वचालित रूप से पाया जाता है?

आइए कार्यान्वयन के साथ शुरू करें:

class A(val n: Int)
object A {
    implicit val ord = new Ordering[A] {
        def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n)
    }
}

इसलिए, विचार करें कि जब आप कॉल करते हैं तो क्या होता है

List(new A(5), new A(2)).sorted

जैसा कि हमने देखा, विधि sortedएक की उम्मीद करती है Ordering[A](वास्तव में, यह एक की उम्मीद करती है Ordering[B], जहां B >: A)। अंदर ऐसी कोई बात नहीं है Ordering, और कोई "स्रोत" प्रकार नहीं है जिस पर देखना है। जाहिर है, यह इसे अंदर पा रहा है A, जो एक प्रकार का तर्क है Ordering

यह भी है कि विभिन्न संग्रह विधियों से CanBuildFromकाम की अपेक्षा कैसे की जाती है : निहितार्थों को ऑब्जेक्ट ऑब्जेक्ट्स के प्रकार के मापदंडों के अंदर पाया जाता है CanBuildFrom

ध्यान दें : Orderingके रूप में परिभाषित किया गया है trait Ordering[T], जहां Tएक प्रकार का पैरामीटर है। इससे पहले, मैंने कहा था कि स्काला ने टाइप के मापदंडों को देखा, जिसका ज्यादा मतलब नहीं है। उपरोक्त के लिए देखा गया निहितार्थ यह है कि वास्तविक प्रकार Ordering[A]कहां Aहै, प्रकार पैरामीटर नहीं: यह एक प्रकार का तर्क है Ordering। स्कैला विनिर्देश के खंड 7.2 देखें।

यह स्काला 2.8.0 के बाद से उपलब्ध है।

नेस्टेड प्रकारों के लिए बाहरी वस्तुएँ

मैंने वास्तव में इसके उदाहरण नहीं देखे हैं। अगर कोई एक साझा कर सकता है तो मैं आभारी रहूंगा। सिद्धांत सरल है:

class A(val n: Int) {
  class B(val m: Int) { require(m < n) }
}
object A {
  implicit def bToString(b: A#B) = "B: %d" format b.m
}
val a = new A(5)
val b = new a.B(3)
val s: String = b  // s == "B: 3"

अन्य आयाम

मुझे पूरा यकीन है कि यह एक मजाक था, लेकिन यह जवाब अप-टू-डेट नहीं हो सकता है। तो यह सवाल मत उठाइए कि क्या हो रहा है, और अगर आपने गौर किया कि यह पुराना हो गया है, तो कृपया मुझे सूचित करें ताकि मैं इसे ठीक कर सकूं।

संपादित करें

संबंधित प्रश्न:


60
यह आपके लिए एक पुस्तक में अपने उत्तरों का उपयोग शुरू करने का समय है, अब तक यह केवल एक साथ रखने की बात है।
पीड्रोफुरला

3
@pedrofurla मुझे पुर्तगाली भाषा में किताब लिखने के लिए माना जाता है। अगर कोई मुझे तकनीकी प्रकाशक से संपर्क कर सकता है ...
डैनियल सी। सोबरल

2
प्रकार के भागों के साथी के पैकेज ऑब्जेक्ट भी खोजे जाते हैं। lampsvn.epfl.ch/trac/scala/ticket/4427
retronym

1
इस मामले में, यह निहित दायरे का हिस्सा है। कॉल साइट को उस पैकेज में नहीं होना चाहिए। यह मेरे लिए आश्चर्यजनक था।
retronym

2
हाँ, तो stackoverflow.com/questions/8623055 को विशेष रूप से शामिल किया गया है, लेकिन मैंने देखा है कि आपने लिखा था "निम्नलिखित सूची को पूर्ववर्ती आदेश में प्रस्तुत किया जाना है ... कृपया रिपोर्ट करें।" मूल रूप से, आंतरिक सूचियों को अनियंत्रित किया जाना चाहिए क्योंकि इन सभी का वजन बराबर होता है (कम से कम 2.10 में)।
यूजीन योकोटा

23

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

यहाँ सूची है:

  • 1) स्थानीय घोषणा, आयात, बाहरी दायरे, वंशानुक्रम, पैकेज ऑब्जेक्ट के माध्यम से वर्तमान आह्वान गुंजाइश को दिखाई दे रहे हैं।
  • 2) निहित गुंजाइश , जिसमें सभी प्रकार की साथी वस्तुएं और पैकेज ऑब्जेक्ट है जो निहित के प्रकार के लिए कुछ संबंध रखता है जिसे हम खोजते हैं (अर्थात प्रकार का पैकेज ऑब्जेक्ट, प्रकार का साथी ऑब्जेक्ट, अपने प्रकार के निर्माता का यदि कोई हो, तो इसके मापदंडों यदि कोई हो, और इसके सुपरपाइप और सुपरट्रैट्स का भी)।

यदि किसी भी स्तर पर हम एक से अधिक निहित हैं, तो इसे हल करने के लिए स्थिर ओवरलोडिंग नियम का उपयोग किया जाता है।


3
इसे बेहतर बनाया जा सकता है यदि आपने कुछ कोड को केवल संकुल, वस्तुओं, लक्षणों और कक्षाओं को परिभाषित करते हुए लिखा है, और जब आप गुंजाइश का उल्लेख करते हैं तो उनके पत्रों का उपयोग करते हैं। किसी भी तरीके की घोषणा करने की आवश्यकता नहीं है - सिर्फ नाम और कौन किसका विस्तार करता है, और किस दायरे में है।
डैनियल सी। सोबरल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.