स्काला में निहित समझ


308

मैं स्काला प्लेफ्रामवर्क ट्यूटोरियल के माध्यम से अपना रास्ता बना रहा था और मुझे कोड के इस स्निपेट से पता चला, जिसने मुझे हैरान कर दिया था:

def newTask = Action { implicit request =>
taskForm.bindFromRequest.fold(
        errors => BadRequest(views.html.index(Task.all(), errors)),
        label => {
          Task.create(label)
          Redirect(routes.Application.tasks())
        } 
  )
}

इसलिए मैंने जांच करने का फैसला किया और इस पद पर आ गया

मैं अभी भी नहीं मिला।

इसमें क्या अंतर है:

implicit def double2Int(d : Double) : Int = d.toInt

तथा

def double2IntNonImplicit(d : Double) : Int = d.toInt

स्पष्ट तथ्य के अलावा उनके पास अलग-अलग तरीके के नाम हैं।

मुझे कब implicitऔर क्यों उपयोग करना चाहिए ?


जवाबों:


391

मैं नीचे दिए गए निहितार्थों के मुख्य उपयोग के मामलों की व्याख्या करूँगा, लेकिन अधिक विवरण के लिए स्काला में प्रोग्रामिंग के संबंधित अध्याय को देखें ।

निहित मापदंडों

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

उदाहरण:

  // probably in a library
class Prefixer(val prefix: String)
def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s

  // then probably in your application
implicit val myImplicitPrefixer = new Prefixer("***")
addPrefix("abc")  // returns "***abc"

सम्यक रूपांतरण

जब संकलक संदर्भ के लिए गलत प्रकार की अभिव्यक्ति पाता है, तो यह Functionएक प्रकार के निहित मूल्य की तलाश करेगा जो इसे टाइपसेक करने की अनुमति देगा। इसलिए यदि कोई Aआवश्यक है और यह एक पाता है B, तो यह B => Aस्कोप में टाइप के एक निहित मूल्य की तलाश करेगा (यह कुछ अन्य स्थानों जैसे Bऔर Aसाथी वस्तुओं की जांच करता है , यदि वे मौजूद हैं)। चूँकि defs Functionवस्तुओं में "एटा-विस्तारित" हो सकता है, एक भी implicit def xyz(arg: B): Aकरेगा।

तो आपके तरीकों के बीच का अंतर यह है कि implicitसंकलक द्वारा आपके लिए एक चिह्नित किया जाएगा जब एक Doubleपाया जाता है लेकिन एक Intकी आवश्यकता होती है।

implicit def doubleToInt(d: Double) = d.toInt
val x: Int = 42.0

के रूप में ही काम करेंगे

def doubleToInt(d: Double) = d.toInt
val x: Int = doubleToInt(42.0)

दूसरे में हमने रूपांतरण मैन्युअल रूप से डाला है; पहले में संकलक ने स्वचालित रूप से समान किया। बाएं हाथ की तरफ एनोटेशन के कारण रूपांतरण आवश्यक है।


प्ले से अपने पहले स्निपेट के बारे में:

कार्यों पर विस्तार से बताया जाता है इस पेज प्ले प्रलेखन से (यह भी देखें एपीआई डॉक्स )। आप उपयोग कर रहे हैं

apply(block: (Request[AnyContent])Result): Action[AnyContent]

पर Actionवस्तु (जो एक ही नाम के लक्षण के लिए साथी है)।

इसलिए हमें तर्क के रूप में एक फंक्शन की आपूर्ति करने की आवश्यकता है, जिसे फॉर्म में शाब्दिक रूप में लिखा जा सकता है

request => ...

एक फ़ंक्शन शाब्दिक में, =>मूल्य घोषणा से पहले का हिस्सा है, और implicitयदि आप चाहते हैं, तो किसी अन्य valघोषणा की तरह चिह्नित किया जा सकता है । यहाँ, request इसकेimplicit लिए चेक टाइप करने के लिए चिह्नित नहीं किया जाना चाहिए , लेकिन ऐसा करने से यह किसी भी तरीके के लिए एक निहित मूल्य के रूप में उपलब्ध होगा जिसे फ़ंक्शन के भीतर इसकी आवश्यकता हो सकती है (और निश्चित रूप से, इसका उपयोग स्पष्ट रूप से भी किया जा सकता है) । इस विशेष मामले में, ऐसा इसलिए किया गया है क्योंकि फॉर्म वर्ग bindFromRequestपर विधि को एक अंतर्निहित तर्क की आवश्यकता होती है।Request


12
जवाब देने के लिए धन्यवाद। अध्याय 21 के लिए लिंक वास्तव में बहुत बढ़िया है। इसकी प्रशंसा करना।
क्लेव

14
बस इसे जोड़ने के लिए, निम्नलिखित वीडियो में निहितार्थों की उत्कृष्ट व्याख्या दी गई है, साथ ही scala की कुछ अन्य विशेषताएं youtube.com/watch?v=IobLWVuD-CQ
शक्ति

ऊपर वीडियो में 24:25 पर जाएं (उन लोगों के लिए जो 55 मिनट के माध्यम से नहीं सुनना चाहते हैं)
पैपीगै

36

चेतावनी: कड़ाई से विवेकपूर्ण तरीके से शामिल है! YMMV ...

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

object HelloWorld {
  case class Text(content: String)
  case class Prefix(text: String)

  implicit def String2Text(content: String)(implicit prefix: Prefix) = {
    Text(prefix.text + " " + content)
  }

  def printText(text: Text): Unit = {
    println(text.content)
  }

  def main(args: Array[String]): Unit = {
    printText("World!")
  }

  // Best to hide this line somewhere below a pile of completely unrelated code.
  // Better yet, import its package from another distant place.
  implicit val prefixLOL = Prefix("Hello")
}

1
Haha। मजाक करने की आदत।
Det

1
मैं हास्य की सराहना करता हूं। इस तरह की बात एक कारण है कि मैंने कई साल पहले स्काला सीखने की कोशिश करना बंद कर दिया था और अब केवल इसे वापस आ रहा हूं। मुझे कभी भी यकीन नहीं था कि मैं जिस कोड को देख रहा था उसमें से कुछ (कई) निहितार्थ आ रहे थे।
मेल्स्टन

7

आपको requestपैरामीटर को क्यों और कब चिह्नित करना चाहिए implicit:

कुछ तरीके जो आप अपनी कार्रवाई के शरीर में उपयोग करेंगे, उनकी एक अंतर्निहित पैरामीटर सूची है, उदाहरण के लिए, Form.scala एक विधि को परिभाषित करता है:

def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... }

आप जरूरी इस पर ध्यान नहीं देते हैं क्योंकि आप बस फोन करेंगे आपको myForm.bindFromRequest()स्पष्ट तर्क प्रदान करने की आवश्यकता नहीं है। नहीं, आप कंपाइलर को किसी भी वैध उम्मीदवार ऑब्जेक्ट को देखने के लिए छोड़ देते हैं , जो हर बार किसी विधि कॉल के दौरान आता है, जिसके लिए अनुरोध के एक उदाहरण की आवश्यकता होती है। चूँकि आपके पास एक अनुरोध उपलब्ध है, इसलिए आपको इसे करने की आवश्यकता है implicit

आप इसे स्पष्ट रूप से निहित उपयोग के लिए उपलब्ध के रूप में चिह्नित करते हैं।

आप संकलक को संकेत देते हैं कि प्ले फ्रेमवर्क द्वारा भेजे गए अनुरोध ऑब्जेक्ट का उपयोग करने के लिए यह "ठीक" है (जिसे हमने "अनुरोध" नाम दिया था, लेकिन जहां भी आवश्यक हो, "धूर्त" पर "r" या "req") का उपयोग किया जा सकता है। ।

myForm.bindFromRequest()

इसे देखें? यह वहाँ नहीं है, लेकिन यह है वहाँ!

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

myForm.bindFromRequest()(request)

निहित के रूप में यह अंकन के बिना, आप चाहते हैं करने के लिए है ऊपर है। इसे ऐसे निहित के रूप में चिह्नित करना जिसे आपको नहीं करना है।

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


2
"इस तरह आप सिर्फ सुंदर कविता कोड लिख सकते हैं।" या, जैसा कि @DanielDinnyes बताते हैं, खूबसूरती से अस्पष्ट कोड। यह ट्रैक करने के लिए एक वास्तविक दर्द हो सकता है जहां से एक निहित आ रहा है और वे वास्तव में कोड को पढ़ने और बनाए रखने के लिए कठिन बना सकते हैं यदि आप सावधान नहीं हैं।
मेल्स्टन

7

Scala में निहित काम करता है :

कनवर्टर

पैरामीटर मान इंजेक्टर

Implicit के 3 प्रकार के उपयोग हैं

  1. अप्रत्यक्ष रूप से रूपांतरण : यह त्रुटि निर्माण कार्य को इच्छित प्रकार में परिवर्तित करता है

    वैल एक्स: स्ट्रिंग = "1"

    वैल वाई: इंट = एक्स

स्ट्रिंग Int का उप प्रकार नहीं है , इसलिए त्रुटि पंक्ति 2 में होती है। त्रुटि को हल करने के लिए कंपाइलर इस तरह की विधि को उस दायरे में देखेगा जिसमें अंतर्निहित कीवर्ड है और स्ट्रिंग को तर्क के रूप में लेता है और एक Int देता है ।

इसलिए

implicit def z(a:String):Int = 2

val x :String = "1"

val y:Int = x // compiler will use z here like val y:Int=z(x)

println(y) // result 2  & no error!
  1. अवैध रूप से रिसीवर रूपांतरण : हम आम तौर पर रिसीवर कॉल ऑब्जेक्ट के गुणों, जैसे। विधियाँ या चर। इसलिए किसी भी संपत्ति को एक रिसीवर द्वारा कॉल करने के लिए संपत्ति को उस रिसीवर के वर्ग / वस्तु का सदस्य होना चाहिए।

    class Mahadi{
    
    val haveCar:String ="BMW"
    
    }
    

    class Johnny{

    val haveTv:String = "Sony"

    }

   val mahadi = new Mahadi



   mahadi.haveTv // Error happening

यहाँ mahadi.haveTv एक त्रुटि उत्पन्न करेगा। क्योंकि स्केला संकलक पहले के लिए दिखेगा haveTv को संपत्ति Mahadi रिसीवर। यह नहीं मिलेगा। दूसरा इसमें एक विधि की तलाश होगी जिसमें निहित कीवर्ड होंगे जो महादानी वस्तु को तर्क के रूप में लेते हैं और जॉनी वस्तु को वापस करते हैं । लेकिन यह यहाँ नहीं है। तो यह त्रुटि पैदा करेगा । लेकिन निम्नलिखित ठीक है।

class Mahadi{

val haveCar:String ="BMW"

}

class Johnny{

val haveTv:String = "Sony"

}

val mahadi = new Mahadi

implicit def z(a:Mahadi):Johnny = new Johnny

mahadi.haveTv // compiler will use z here like new Johnny().haveTv

println(mahadi.haveTv)// result Sony & no error
  1. अवैध रूप से पैरामीटर इंजेक्शन : यदि हम एक विधि कहते हैं और इसके पैरामीटर मान को पारित नहीं करते हैं, तो यह एक त्रुटि का कारण होगा। स्कैला संकलक इस तरह काम करता है - पहले मूल्य पारित करने की कोशिश करेगा, लेकिन इसे पैरामीटर के लिए कोई प्रत्यक्ष मूल्य नहीं मिलेगा।

    def x(a:Int)= a
    
    x // ERROR happening
    

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

def x(implicit a:Int)= a

x // error happening here

Slove करने के लिए इस समस्या को संकलक एक के लिए दिखेगा निहित वैल होने इंट के प्रकार क्योंकि पैरामीटर एक है निहित कीवर्ड

def x(implicit a:Int)=a

implicit val z:Int =10

x // compiler will use implicit like this x(z)
println(x) // will result 10 & no error.

एक और उदाहरण:

def l(implicit b:Int)

def x(implicit a:Int)= l(a)

हम इसे भी लिख सकते हैं-

def x(implicit a:Int)= l

क्योंकि मैं एक है अंतर्निहित पैरामीटर और के दायरे में विधि एक्स के शरीर , वहाँ एक है अंतर्निहित स्थानीय चर ( मापदंडों स्थानीय चर रहे हैं ) एक जिनमें से पैरामीटर है एक्स , तो में एक्स के शरीर विधि विधि-हस्ताक्षर ल रों अंतर्निहित तर्क मूल्य है द्वारा दायर एक्स विधि के स्थानीय अंतर्निहित चर (पैरामीटर) aपरोक्ष

इसलिए

 def x(implicit a:Int)= l

इस तरह संकलक में हो जाएगा

def x(implicit a:Int)= l(a)

एक और उदाहरण:

def c(implicit k:Int):String = k.toString

def x(a:Int => String):String =a

x{
x => c
}

यह, त्रुटि का कारण है क्योंकि जाएगा में एक्स {x => c} स्पष्ट रूप-मान-गुजर तर्क या अस्पष्ट वैल में जरूरत दायरे में

जब हम विधि x को कॉल करते हैं तो हम फ़ंक्शन शाब्दिक पैरामीटर को स्पष्ट रूप से अंतर्निहित कर सकते हैं

x{
implicit x => c // the compiler will set the parameter of c like this c(x)
}

प्ले-फ्रेमवर्क की एक्शन विधि में इसका उपयोग किया गया है

in view folder of app the template is declared like
@()(implicit requestHreader:RequestHeader)

in controller action is like

def index = Action{
implicit request =>

Ok(views.html.formpage())  

}

यदि आपने अनुरोध पैरामीटर का उल्लेख स्पष्ट रूप से नहीं किया है तो आपको लिखा जाना चाहिए-

def index = Action{
request =>

Ok(views.html.formpage()(request))  

}

4

इसके अलावा, उपरोक्त मामले में only oneनिहित कार्य होना चाहिए जिसका प्रकार है double => Int। अन्यथा, संकलक भ्रमित हो जाता है और ठीक से संकलन नहीं करेगा।

//this won't compile

implicit def doubleToInt(d: Double) = d.toInt
implicit def doubleToIntSecond(d: Double) = d.toInt
val x: Int = 42.0

0

स्केला में इम्प्लाइकिट्स का एक बहुत ही मूल उदाहरण।

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

val value = 10
implicit val multiplier = 3
def multiply(implicit by: Int) = value * by
val result = multiply // implicit parameter wiil be passed here
println(result) // It will print 30 as a result

नोट: यहाँ multiplierसंक्षेप में फ़ंक्शन में पारित किया जाएगा multiply। फ़ंक्शन कॉल में गुम पैरामीटर को वर्तमान स्कोप में टाइप करके देखा जाता है जिसका अर्थ है कि कोड संकलित नहीं होगा यदि स्कोप में टाइप इंट का कोई निहित चर नहीं है।

निहित रूपांतरण :

implicit def convert(a: Double): Int = a.toInt
val res = multiply(2.0) // Type conversions with implicit functions
println(res)  // It will print 20 as a result

नोट: जब हम multiplyफ़ंक्शन को एक डबल मान पास करते हैं, तो कंपाइलर वर्तमान कार्यक्षेत्र में रूपांतरण निहित फ़ंक्शन को खोजने की कोशिश करेगा, जो कि (जैसे फ़ंक्शन फ़ंक्शन पैरामीटर को स्वीकार करता है) में परिवर्तित हो Intजाता है । यदि कोई निहित कार्य नहीं है, तो संकलक कोड संकलित नहीं करेगा। DoublemultiplyIntconvert


0

मेरे पास आपके जैसा ही प्रश्न था और मुझे लगता है कि मुझे यह साझा करना चाहिए कि मैंने इसे कैसे कुछ सरल उदाहरणों से समझना शुरू किया (ध्यान दें कि यह केवल सामान्य उपयोग के मामलों को कवर करता है)।

Scala के उपयोग में दो सामान्य उपयोग के मामले हैं implicit

  • एक चर पर इसका उपयोग करना
  • एक फंक्शन पर इसका उपयोग करना

उदाहरण इस प्रकार हैं

एक चर पर इसका उपयोग करना । जैसा कि आप देख सकते हैं, यदि implicitकीवर्ड अंतिम पैरामीटर सूची में उपयोग किया जाता है, तो निकटतम चर का उपयोग किया जाएगा।

// Here I define a class and initiated an instance of this class
case class Person(val name: String)
val charles: Person = Person("Charles")

// Here I define a function
def greeting(words: String)(implicit person: Person) = person match {
  case Person(name: String) if name != "" => s"$name, $words"
    case _ => "$words"
}

greeting("Good morning") // Charles, Good moring

val charles: Person = Person("")
greeting("Good morning") // Good moring

एक फंक्शन पर इसका उपयोग करना । जैसा कि आप देख सकते हैं, यदि implicitफ़ंक्शन पर उपयोग किया जाता है, तो निकटतम प्रकार के रूपांतरण विधि का उपयोग किया जाएगा।

val num = 10 // num: Int (of course)

// Here I define a implicit function
implicit def intToString(num: Int) = s"$num -- I am a String now!"

val num = 10 // num: Int (of course). Nothing happens yet.. Compiler believes you want 10 to be an Int

// Util...
val num: String = 10 // Compiler trust you first, and it thinks you have `implicitly` told it that you had a way to covert the type from Int to String, which the function `intToString` can do!
// So num is now actually "10 -- I am a String now!"
// console will print this -> val num: String = 10 -- I am a String now!

उम्मीद है कि यह मदद कर सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.