क्या करें <: <, <% <, और =: = का मतलब है स्कैला 2.8 में, और वे कहाँ प्रलेखित हैं?


201

मैं पूर्वनिर्धारित के लिए एपीआई डॉक्स में देख सकता हूं कि वे एक सामान्य फ़ंक्शन प्रकार (से) => टू के उपवर्ग हैं, लेकिन यह सब कहता है। उम्म क्या? हो सकता है कि कहीं दस्तावेज हो, लेकिन खोज इंजन "नाम" जैसे "<: <" को बहुत अच्छी तरह से संभाल नहीं करता है, इसलिए मैं इसे खोजने में सक्षम नहीं हूं।

अनुवर्ती सवाल: मुझे इन कायरता प्रतीकों / वर्गों का उपयोग कब करना चाहिए, और क्यों?


6
यहां एक संबंधित प्रश्न है जो आपके प्रश्न का आंशिक रूप से कम से कम उत्तर दे सकता है: stackoverflow.com/questions/2603003/operator-in-scala
Yardena

13
symbolhound.com आपका कोड खोज मित्र है :)
ron

क्या हास्केल की typeclassइन संचालकों का काम है? उदाहरण: compare :: Ord a => a -> a -> Ordering? मैं इस स्केल अवधारणा को इसके हास्केल काउंटर-भाग के संबंध में समझने की कोशिश कर रहा हूं।
केविन मेरेडिथ

जवाबों:


217

इन्हें सामान्यीकृत प्रकार की कमी कहा जाता है । वे आपको एक प्रकार के पैरामीटर वाले वर्ग या विशेषता के भीतर से इसके एक प्रकार के मापदंडों को आगे बढ़ाने के लिए अनुमति देते हैं । यहाँ एक उदाहरण है:

case class Foo[A](a:A) { // 'A' can be substituted with any type
    // getStringLength can only be used if this is a Foo[String]
    def getStringLength(implicit evidence: A =:= String) = a.length
}

निहित तर्क evidenceसंकलक द्वारा आपूर्ति की जाती है, iff Aहै String। आप एक के रूप में यह सोच सकते हैं सबूत है कि Aहै String--The तर्क ही महत्वपूर्ण नहीं है, केवल जानते हुए भी कि यह मौजूद है। [संपादित करें: ठीक है, तकनीकी रूप से यह वास्तव में महत्वपूर्ण है क्योंकि यह एक अंतर्निहित रूपांतरण का प्रतिनिधित्व करता Aहै String, जो कि आपको कॉल करने की अनुमति देता है a.lengthऔर आप पर संकलक चिल्लाना नहीं है]

अब मैं इसका उपयोग इस तरह कर सकता हूं:

scala> Foo("blah").getStringLength
res6: Int = 4

लेकिन मैं एक साथ उपयोग करने की कोशिश की है, तो Fooकुछ युक्त एक अन्य की तुलना में String:

scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]

आप उस त्रुटि को पढ़ सकते हैं जैसे "साक्ष्य नहीं पा सके कि Int == स्ट्रिंग" ... यही होना चाहिए! सामान्य आवश्यकता से अधिक के प्रकार पर और प्रतिबंधgetStringLength लगा रहा है ; अर्थात्, आप केवल एक पर आह्वान कर सकते हैं । यह बाधा संकलन-समय पर लागू की जाती है, जो शांत है!AFoogetStringLengthFoo[String]

<:<और <%<इसी तरह काम करते हैं, लेकिन मामूली बदलाव के साथ:

  • A =:= B मतलब A बिल्कुल B होना चाहिए
  • A <:< Bए का मतलब बी का एक उपप्रकार होना चाहिए ( सरल प्रकार की बाधा के अनुरूप <:)
  • A <%< Bइसका मतलब है कि ए को बी के रूप में देखा जा सकता है , संभवतः अंतर्निहित रूपांतरण के माध्यम से (सरल प्रकार की बाधा के अनुरूप <%)

@Retronym द्वारा यह स्निपेट इस बात का एक अच्छा विवरण है कि इस तरह की चीज़ों को कैसे पूरा किया जाता था और कैसे सामान्यीकृत प्रकार की बाधाएं अब इसे आसान बनाती हैं।

परिशिष्ट

आपके अनुवर्ती प्रश्न का उत्तर देने के लिए, माना जाता है कि मैंने जो उदाहरण दिया है वह बहुत ही महत्वपूर्ण है और स्पष्ट रूप से उपयोगी नहीं है। लेकिन एक List.sumIntsविधि की तरह कुछ परिभाषित करने के लिए इसका उपयोग करने की कल्पना करें , जो पूर्णांक की एक सूची को जोड़ता है। आप इस विधि को किसी भी पुराने List, केवल एक पर लागू करने की अनुमति नहीं देना चाहते हैं List[Int]। हालाँकि, Listटाइप कंस्ट्रक्टर इतना संकुचित नहीं हो सकता है; आप अभी भी स्ट्रिंग्स, फॉक्स, बार, और व्हाट्सन की सूची में सक्षम होना चाहते हैं। तो एक सामान्यीकृत प्रकार की बाधा पर रखकर sumInts, आप यह सुनिश्चित कर सकते हैं कि बस उस विधि में एक अतिरिक्त बाधा है कि इसका उपयोग केवल ए के लिए किया जा सकता है List[Int]। अनिवार्य रूप से आप कुछ विशेष प्रकार की सूचियों के लिए विशेष-केस कोड लिख रहे हैं।


3
ठीक है, ठीक है, लेकिन वहाँ भी इसी नाम से तरीके हैं Manifest, जिस पर आपने उल्लेख नहीं किया है।
डैनियल सी। सोबरल

3
पर तरीकों Manifestहैं <:<और >:>केवल ... के बाद से ओपी उल्लेख सामान्यीकृत प्रकार की कमी की सटीक रूप से 3 किस्मों, मुझे लगता है कि क्या वह में रुचि थी यह सोचते हैं रहा हूँ।
टॉम Crockett

12
@IttayD: यह बहुत चालाक है ... class =:=[From, To] extends From => To, जिसका अर्थ है उस प्रकार के एक अंतर्निहित मूल्य From =:= Toवास्तव में एक अंतर्निहित है रूपांतरण से Fromकरने के लिए To। इसलिए प्रकार के एक अंतर्निहित पैरामीटर को स्वीकार करके A =:= Stringआप कह रहे हैं कि Aअंतर्निहित रूप से परिवर्तित किया जा सकता है String। यदि आप क्रम बदल गया है और बनाया अंतर्निहित तर्क प्रकार का होना String =:= A, यह काम नहीं है, क्योंकि इस से एक अंतर्निहित रूपांतरण किया जाएगा Stringकरने के लिए A
टॉम क्रोकेट

25
क्या उन तीन-चरित्र प्रतीकों के नाम हैं? स्काला के प्रतीक सूप के साथ मेरी समस्या यह है कि वे मौखिक रूप से बात करना कठिन है, और चर्चाओं और उनके उपयोग के उदाहरणों को खोजने के लिए Google या किसी अन्य खोज इंजन का उपयोग करना व्यावहारिक रूप से असंभव है।
गिगाट्रॉन

4
@Andrea नोप, यह केवल तभी काम करेगा जब प्रकार बिल्कुल समान हों। ध्यान दें कि मैंने कहा है कि From =:= Toस्कोप के प्रकार का निहित मूल्य होने का तात्पर्य है कि आपके पास एक अंतर्निहित रूपांतरण है From => To, लेकिन निहितार्थ पीछे की ओर नहीं चलता है; एक अंतर्निहित रूपांतरण होने का अर्थ A => Bयह नहीं है कि आपके पास इसका उदाहरण है A =:= B=:=एक सीलबंद अमूर्त वर्ग है scala.Predef, और इसमें केवल एक सार्वजनिक रूप से उजागर उदाहरण है, जो निहित है, और प्रकार का है A =:= A। तो आपको गारंटी है कि प्रकार का एक निहित मूल्य A =:= Bइस तथ्य का गवाह है कि AऔरB समान हैं।
टॉम क्रॉकेट

55

पूर्ण उत्तर नहीं (दूसरों ने पहले ही इसका उत्तर दिया है), मैं बस निम्नलिखित नोट करना चाहता था, जो शायद वाक्यविन्यास को बेहतर ढंग से समझने में मदद करता है: जिस तरह से आप आमतौर पर इन "ऑपरेटरों" का उपयोग करते हैं, उदाहरण के लिए श्रोणि के उदाहरण में:

def getStringLength(implicit evidence: A =:= String)

प्रकार ऑपरेटरों के लिए स्काला के वैकल्पिक इन्फिक्स सिंटैक्स का उपयोग करता है ।

तो, A =:= Stringजैसा है =:=[A, String](और =:=एक फैंसी दिखने वाले नाम के साथ सिर्फ एक वर्ग या विशेषता है)। ध्यान दें कि यह वाक्यविन्यास "नियमित" कक्षाओं के साथ भी काम करता है, उदाहरण के लिए आप लिख सकते हैं:

val a: Tuple2[Int, String] = (1, "one")

इस तरह:

val a: Int Tuple2 String = (1, "one")

यह विधि कॉल, साथ "सामान्य" के लिए दोनों वाक्यविन्यास के समान है .और ()और ऑपरेटर वाक्य रचना।


2
अपवोट करने की आवश्यकता है क्योंकि makes use of Scala's alternative infix syntax for type operators.इस स्पष्टीकरण को पूरी तरह से याद किए बिना जिसके बिना पूरी बात समझ में नहीं आती है
Ovidiu Dolha

39

इन निर्माणों को समझने के लिए अन्य उत्तर पढ़ें। यहाँ है जब आप उन्हें इस्तेमाल करना चाहिए। आप उनका उपयोग तब करते हैं जब आपको केवल विशिष्ट प्रकार के लिए एक विधि बनाने की आवश्यकता होती है।

यहाँ एक उदाहरण है। मान लीजिए कि आप एक सजातीय जोड़ी को इस तरह परिभाषित करना चाहते हैं:

class Pair[T](val first: T, val second: T)

अब आप smallerइस तरह एक विधि जोड़ना चाहते हैं :

def smaller = if (first < second) first else second

अगर Tआदेश दिया जाए तो ही काम होगा । आप पूरी कक्षा को प्रतिबंधित कर सकते हैं:

class Pair[T <: Ordered[T]](val first: T, val second: T)

लेकिन यह शर्म की बात है - जब Tआदेश नहीं दिया जाता है तो कक्षा के लिए उपयोग किया जा सकता है । एक प्रकार की बाधा के साथ, आप अभी भी smallerविधि को परिभाषित कर सकते हैं :

def smaller(implicit ev: T <:< Ordered[T]) = if (first < second) first else second

जब तक आप फोन नहीं करते हैंPair[File] , तब तक ए , कहना ठीक है smaller पर है।

के मामले में Option, कार्यान्वयनकर्ताओं को एक orNullविधि चाहिए थी, भले ही इसका कोई मतलब न हो Option[Int]। एक प्रकार की बाधा का उपयोग करके, सब ठीक है। आप a orNullपर उपयोग कर सकते हैं Option[String], और Option[Int]जब तक आप इस orNullपर कॉल नहीं करते हैं, तब तक आप इसका निर्माण कर सकते हैं और इसका उपयोग कर सकते हैं । यदि आप कोशिश करते हैं Some(42).orNull, तो आपको आकर्षक संदेश मिलता है

 error: Cannot prove that Null <:< Int

2
मुझे पता है कि इस उत्तर के बाद यह साल है, लेकिन मैं इसके लिए उपयोग के मामलों की तलाश कर रहा हूं <:<, और मुझे लगता है कि Orderedउदाहरण अब और सम्मोहक नहीं है क्योंकि अब आप Orderingटाइपकास्ट का उपयोग करेंगे बजाय Orderedविशेषता के। की तरह कुछ: def smaller(implicit ord: Ordering[T]) = if (ord.lt(first, second)) first else second
ebruchez

1
@ebruchez: अनमोडिफाइड स्काला में यूनियन टाइप्स को एन्कोडिंग करने के लिए एक केस केस है, देखें milessabin.com/blog/2011/06/09/scala-union-types-curry-howard

17

यह इस बात पर निर्भर करता है कि उनका उपयोग कहां किया जा रहा है। अक्सर, जब अंतर्निहित प्रकार के मापदंडों की घोषणा करते समय उपयोग किया जाता है, तो वे कक्षाएं होती हैं। वे दुर्लभ उदाहरणों में भी वस्तु हो सकते हैं। अंत में, वे Manifestवस्तुओं पर ऑपरेटर हो सकते हैं। वे scala.Predefपहले दो मामलों में अंदर परिभाषित हैं , हालांकि विशेष रूप से अच्छी तरह से प्रलेखित नहीं हैं।

वे कक्षाओं के बीच संबंधों का परीक्षण करने का एक तरीका प्रदान करते हैं, ठीक उसी तरह जैसे <:और <%करते हैं, उन स्थितियों में जब उत्तरार्द्ध का उपयोग नहीं किया जा सकता है।

सवाल के लिए "मुझे उनका उपयोग कब करना चाहिए?", इसका उत्तर आपको नहीं होना चाहिए, जब तक कि आपको पता नहीं होना चाहिए। :-) संपादित करें : ठीक है, ठीक है, यहाँ पुस्तकालय से कुछ उदाहरण हैं। पर Either, आपके पास:

/**
  * Joins an <code>Either</code> through <code>Right</code>.
  */
 def joinRight[A1 >: A, B1 >: B, C](implicit ev: B1 <:< Either[A1, C]): Either[A1, C] = this match {
   case Left(a)  => Left(a)
   case Right(b) => b
 }

 /**
  * Joins an <code>Either</code> through <code>Left</code>.
  */
 def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]): Either[C, B1] = this match {
   case Left(a)  => a
   case Right(b) => Right(b)
 }

पर Option, आपके पास:

def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null

आपको संग्रह पर कुछ अन्य उदाहरण मिलेंगे।


क्या :-)इनमें से एक और है? और मैं सहमत हूँ कि आपका जवाब "मुझे उनका उपयोग कब करना चाहिए?" एक महान कई चीजों के लिए लागू होता है।
माइक मिलर

"वे कक्षाओं के बीच संबंधों का परीक्षण करने का एक तरीका प्रदान करने के लिए हैं" <- बहुत सामान्य सहायक होने के लिए
जेफ

3
"सवाल के लिए के रूप में" मुझे उनका उपयोग कब करना चाहिए? ", जवाब आपको नहीं होना चाहिए, जब तक कि आपको पता नहीं होना चाहिए।" <- इसीलिए मैं पूछ रहा हूं। मैं अपने लिए वह निश्चय करने में सक्षम होना चाहता हूं।
जेफ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.