Scala3 में कक्षाएँ टाइप करें: एक शुरुआती मार्गदर्शिका | खाता बही

Scala3 में कक्षाएँ टाइप करें: एक शुरुआती मार्गदर्शिका | खाता बही

Scala3 में कक्षाएँ टाइप करें: एक शुरुआती मार्गदर्शिका | लेजर प्लेटोब्लॉकचेन डेटा इंटेलिजेंस। लंबवत खोज. ऐ.

यह दस्तावेज़ शुरुआती Scala3 डेवलपर के लिए है जो पहले से ही Scala गद्य में पारंगत है, लेकिन सभी `implicits` और कोड में पैरामीटरयुक्त लक्षण।

यह दस्तावेज़ बताता है कि क्यों, कैसे, कहाँ और कब प्रकार कक्षाएं (टीसी).

इस दस्तावेज़ को पढ़ने के बाद शुरुआती Scala3 डेवलपर को उपयोग करने के लिए ठोस ज्ञान प्राप्त होगा और इसके स्रोत कोड में गोता लगाएंगे बहुत स्काला पुस्तकालयों का और मुहावरेदार स्काला कोड लिखना शुरू करें।

आइए क्यों से शुरू करें...

अभिव्यक्ति की समस्या

1998 में, फिलिप वाडलर ने कहा कि "अभिव्यक्ति समस्या एक पुरानी समस्या का नया नाम है"। यह सॉफ़्टवेयर एक्स्टेंसिबिलिटी की समस्या है। मिस्टर वाडलर के लेखन के अनुसार, अभिव्यक्ति समस्या के समाधान को निम्नलिखित नियमों का पालन करना चाहिए:

  • नियम 1: के कार्यान्वयन की अनुमति दें मौजूदा व्यवहार (स्कैला विशेषता के बारे में सोचें) को लागू किया जाना है नये अभ्यावेदन (एक केस क्लास के बारे में सोचें)
  • नियम 2: के कार्यान्वयन की अनुमति दें नये व्यवहार पर लागू किया जाना है मौजूदा अभ्यावेदन
  • नियम 3: इसे खतरे में नहीं डालना चाहिए प्रकार की सुरक्षा
  • नियम 4: इसे पुनः संकलित करने की आवश्यकता नहीं होनी चाहिए मौजूदा कोड

इस समस्या का समाधान इस लेख का मुख्य उद्देश्य होगा।

नियम 1: नए प्रतिनिधित्व पर मौजूदा व्यवहार का कार्यान्वयन

किसी भी ऑब्जेक्ट ओरिएंटेड भाषा में नियम 1 के लिए एक बेक्ड-इन समाधान होता है उपप्रकार बहुरूपता. आप किसी भी `को सुरक्षित रूप से कार्यान्वित कर सकते हैंtrait` पर निर्भरता में परिभाषित `classनिर्भरता को पुनः संकलित किए बिना, अपने स्वयं के कोड में। आइए इसे क्रियान्वित रूप में देखें:

स्काला

def todo = 42
type Height = Int
type Block = Int

object Lib1:
 trait Blockchain:
 def getBlock(height: Height): Block

 case class Ethereum() extends Blockchain:
 override def getBlock(height: Height) = todo

 case class Bitcoin() extends Blockchain:
 override def getBlock(height: Height) = todo


object Lib2:
 import Lib1.*

 case class Polkadot() extends Blockchain:
 override def getBlock(height: Height): Block = todo

val eth = Lib1.Ethereum()
val btc = Lib1.Bitcoin()
val dot = Lib2.Polkadot()

इस काल्पनिक उदाहरण में, लाइब्रेरी `Lib1' (पंक्ति 5) एक विशेषता को परिभाषित करती हैBlockchain` (पंक्ति 6) इसके 2 कार्यान्वयन के साथ (पंक्ति 9 और 12)। `Lib1`इस सभी दस्तावेज़ में वही रहेगा (नियम 4 का प्रवर्तन)।

`Lib2` (पंक्ति 15) मौजूदा व्यवहार को लागू करता है `Blockchain`एक नई कक्षा पर`Polkadot' (नियम 1) एक प्रकार से सुरक्षित (नियम 3) तरीके से, बिना पुन: संकलित किएLib1` (नियम 4). 

नियम 2: मौजूदा अभ्यावेदन पर लागू होने वाले नए व्यवहारों का कार्यान्वयन

आइए `में कल्पना करेंLib2'हम एक नया व्यवहार चाहते हैं'lastBlock`प्रत्येक के लिए विशेष रूप से लागू किया जाना है`Blockchain`.

पहली बात जो दिमाग में आती है वह है पैरामीटर के प्रकार के आधार पर एक बड़ा स्विच बनाना।

स्काला

def todo = 42
type Height = Int
type Block = Int

object Lib1:
 trait Blockchain:
 def getBlock(height: Height): Block

 case class Ethereum() extends Blockchain:
 override def getBlock(height: Height) = todo

 case class Bitcoin() extends Blockchain:
 override def getBlock(height: Height) = todo

object Lib2:
 import Lib1.*

 case class Polkadot() extends Blockchain:
 override def getBlock(height: Height): Block = todo

 def lastBlock(blockchain: Blockchain): Block = blockchain match
 case _:Ethereum => todo
 case _:Bitcoin => todo
 case _:Polkadot => todo
 

object Lib3:
 import Lib1.*

 case class Polygon() extends Blockchain:
 override def getBlock(height: Height): Block = todo

import Lib1.*, Lib2.*, Lib3.*
println(lastBlock(Bitcoin()))
println(lastBlock(Ethereum()))
println(lastBlock(Polkadot()))
println(lastBlock(Polygon()))

यह समाधान प्रकार आधारित बहुरूपता का एक कमजोर पुन: कार्यान्वयन है जो पहले से ही भाषा में बेक किया हुआ है!

`Lib1` को अछूता छोड़ दिया गया है (याद रखें, इस दस्तावेज़ में नियम 4 लागू किया गया है)। 

समाधान `में कार्यान्वित किया गयाLib2` है ठीक है जब तक `में एक और ब्लॉकचेन पेश नहीं किया जाताLib3`. यह प्रकार के सुरक्षा नियम (नियम 3) का उल्लंघन करता है क्योंकि यह कोड लाइन 37 पर रनटाइम पर विफल हो जाता है। और 'को संशोधित करनाLib2`नियम 4 का उल्लंघन होगा।

एक अन्य समाधान ` का उपयोग करना हैextension`.

स्काला

def todo = 42
type Height = Int
type Block = Int

object Lib1:
 trait Blockchain:
 def getBlock(height: Height): Block

 case class Ethereum() extends Blockchain:
 override def getBlock(height: Height) = todo

 case class Bitcoin() extends Blockchain:
 override def getBlock(height: Height) = todo

object Lib2:
 import Lib1.*

 case class Polkadot() extends Blockchain:
 override def getBlock(height: Height): Block = todo

 def lastBlock(): Block = todo

 extension (eth: Ethereum) def lastBlock(): Block = todo

 extension (btc: Bitcoin) def lastBlock(): Block = todo

import Lib1.*, Lib2.*
println(Bitcoin().lastBlock())
println(Ethereum().lastBlock())
println(Polkadot().lastBlock())

def polymorphic(blockchain: Blockchain) =
 // blockchain.lastBlock()
 ???

`Lib1` को अछूता छोड़ दिया गया है (पूरे दस्तावेज़ में नियम 4 का प्रवर्तन)। 

`Lib2`इसके प्रकार (पंक्ति 21) के लिए व्यवहार को परिभाषित करता है और मौजूदा प्रकारों (पंक्ति 23 और 25) के लिए `एक्सटेंशन` को परिभाषित करता है।

पंक्तियाँ 28-30, प्रत्येक कक्षा में नए व्यवहार का उपयोग किया जा सकता है। 

लेकिन इस नए व्यवहार को बहुरूपी (पंक्ति 32) कहने का कोई तरीका नहीं है। ऐसा करने का कोई भी प्रयास संकलन त्रुटियों (पंक्ति 33) या टाइप आधारित स्विच की ओर ले जाता है। 

यह नियम n°2 पेचीदा है। हमने इसे बहुरूपता की अपनी परिभाषा और 'विस्तार' युक्ति के साथ लागू करने का प्रयास किया। और वह अजीब था.

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

टाइप क्लास पैटर्न

टाइप क्लास (संक्षेप में टीसी) पैटर्न रेसिपी में 3 चरण हैं। 

  1. एक नया व्यवहार परिभाषित करें
  2. व्यवहार को क्रियान्वित करें
  3. व्यवहार का प्रयोग करें

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

1. नये व्यवहार को परिभाषित करें
स्काला

object Lib2:
 import Lib1.*

 trait LastBlock[A]:
 def lastBlock(instance: A): Block

`Lib1`, एक बार फिर, अछूता छोड़ दिया गया है।

नया व्यवहार is टीसी विशेषता द्वारा साकार हुई। विशेषता में परिभाषित कार्य उस व्यवहार के कुछ पहलुओं को लागू करने का एक तरीका है।

पैरामीटर `A` उस प्रकार का प्रतिनिधित्व करता है जिस पर हम व्यवहार लागू करना चाहते हैं, जो ` के उपप्रकार हैंBlockchain`हमारे मामले में.

कुछ टिप्पणियाँ:

  • यदि आवश्यक हो, तो पैरामीटरयुक्त प्रकार `A`स्कैला प्रकार प्रणाली द्वारा और अधिक बाधित किया जा सकता है। उदाहरण के लिए, हम ` लागू कर सकते हैंA` एक होना `Blockchain`. 
  • साथ ही, टीसी में और भी कई कार्य घोषित हो सकते हैं।
  • अंत में, प्रत्येक फ़ंक्शन में कई और मनमाने पैरामीटर हो सकते हैं।

लेकिन आइए पठनीयता के लिए चीजों को सरल रखें।

2. व्यवहार को क्रियान्वित करें
स्काला

object Lib2:
 import Lib1.*

 trait LastBlock[A]:
 def lastBlock(instance: A): Block

 val ethereumLastBlock = new LastBlock[Ethereum]:
 def lastBlock(eth: Ethereum) = eth.lastBlock

 val bitcoinLastBlock = new LastBlock[Bitcoin]:
 def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")

प्रत्येक प्रकार के लिए नया `LastBlock`व्यवहार अपेक्षित है, उस व्यवहार का एक विशिष्ट उदाहरण है। 

`Ethereum`कार्यान्वयन पंक्ति 22 की गणना` से की जाती हैeth`उदाहरण पैरामीटर के रूप में पारित किया गया। 

का कार्यान्वयनLastBlock` के लिए `Bitcoin`पंक्ति 25 एक अप्रबंधित आईओ के साथ कार्यान्वित किया गया है और इसके पैरामीटर का उपयोग नहीं करता है।

तो, `Lib2`नया व्यवहार लागू करता है`LastBlock` के लिए `Lib1` कक्षाएं।

3. व्यवहार का प्रयोग करें
स्काला

object Lib2:
 import Lib1.*

 trait LastBlock[A]:
 def lastBlock(instance: A): Block

 val ethereumLastBlock = new LastBlock[Ethereum]:
 def lastBlock(eth: Ethereum) = eth.lastBlock

 val bitcoinLastBlock = new LastBlock[Bitcoin]:
 def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")

import Lib1.*, Lib2.*

def useLastBlock[A](instance: A, behavior: LastBlock[A]) =
 behavior.lastBlock(instance)

println(useLastBlock(Ethereum(lastBlock = 2), ethereumLastBlock))
println(useLastBlock(Bitcoin(), bitcoinLastBlock))

पंक्ति 30`useLastBlock` का एक उदाहरण उपयोग करता हैA` और `LastBlock`उस उदाहरण के लिए व्यवहार परिभाषित किया गया है।

पंक्ति 33`useLastBlock` को ` के उदाहरण के साथ बुलाया जाता हैEthereum` और ` का कार्यान्वयनLastBlock`में परिभाषित किया गया हैLib2`. ध्यान दें कि `के किसी भी वैकल्पिक कार्यान्वयन को पारित करना संभव हैLastBlock[A]' (सोचिए निर्भरता अन्तःक्षेपण).

`useLastBlock' प्रतिनिधित्व (वास्तविक ए) और उसके व्यवहार के बीच का गोंद है। डेटा और व्यवहार को अलग किया जाता है, कार्यात्मक प्रोग्रामिंग इसी की वकालत करती है।

चर्चा

आइए अभिव्यक्ति समस्या के नियमों को दोबारा समझें:

  • नियम 1: के कार्यान्वयन की अनुमति दें मौजूदा व्यवहार  पर लागू किया जाना है नई कक्षाएं
  • नियम 2: के कार्यान्वयन की अनुमति दें नये व्यवहार पर लागू किया जाना है मौजूदा कक्षाएं
  • नियम 3: इसे खतरे में नहीं डालना चाहिए प्रकार की सुरक्षा
  • नियम 4: इसे पुनः संकलित करने की आवश्यकता नहीं होनी चाहिए मौजूदा कोड

नियम 1 को उपप्रकार बहुरूपता के साथ बॉक्स से बाहर हल किया जा सकता है।

अभी प्रस्तुत टीसी पैटर्न (पिछला स्क्रीनशॉट देखें) नियम 2 को हल करता है। यह सुरक्षित प्रकार है (नियम 3) और हमने कभी नहीं छुआLib1` (नियम 4). 

हालाँकि कई कारणों से इसका उपयोग करना अव्यावहारिक है:

  • पंक्ति 33-34 में हमें व्यवहार को उसके उदाहरण के साथ स्पष्ट रूप से पारित करना होगा। यह एक अतिरिक्त उपरिव्यय है. हमें बस `लिखना चाहिएuseLastBlock(Bitcoin())`.
  • पंक्ति 31 का वाक्यविन्यास असामान्य है। हम संक्षिप्त और अधिक वस्तु उन्मुखी लिखना पसंद करेंगेinstance.lastBlock()` कथन.

आइए व्यावहारिक टीसी उपयोग के लिए कुछ स्काला सुविधाओं पर प्रकाश डालें। 

उन्नत डेवलपर अनुभव

स्काला में सुविधाओं और वाक्यात्मक शर्करा का एक अनूठा सेट है जो टीसी को डेवलपर्स के लिए वास्तव में सुखद अनुभव बनाता है।

निहितार्थ

अंतर्निहित दायरा संकलन समय पर हल किया गया एक विशेष दायरा है जहां किसी दिए गए प्रकार का केवल एक उदाहरण मौजूद हो सकता है। 

एक प्रोग्राम `के साथ अंतर्निहित दायरे में एक उदाहरण रखता हैgiven`कीवर्ड. वैकल्पिक रूप से एक प्रोग्राम कीवर्ड के साथ अंतर्निहित दायरे से एक उदाहरण पुनर्प्राप्त कर सकता हैusing`.

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

यह वैश्विक दायरे से अलग है क्योंकि: 

  1. इसे प्रासंगिक रूप से हल किया गया है। किसी प्रोग्राम के दो स्थान अंतर्निहित दायरे में एक ही दिए गए प्रकार के उदाहरण का उपयोग कर सकते हैं, लेकिन वे दो उदाहरण भिन्न हो सकते हैं।
  2. दृश्य के पीछे कोड अंतर्निहित उपयोग तक पहुंचने तक कार्य करने के लिए अंतर्निहित तर्क फ़ंक्शन पास कर रहा है। यह वैश्विक मेमोरी स्पेस का उपयोग नहीं कर रहा है।

टाइप क्लास में वापस जा रहे हैं! आइए ठीक वैसा ही उदाहरण लें।

स्काला

def todo = 42
type Height = Int
type Block = Int
def http(uri: String): Block = todo

object Lib1:
 trait Blockchain:
 def getBlock(height: Height): Block

 case class Ethereum() extends Blockchain:
 override def getBlock(height: Height) = todo

 case class Bitcoin() extends Blockchain:
 override def getBlock(height: Height) = todo

`Lib1` वही असंशोधित कोड है जिसे हमने पहले परिभाषित किया था। 

स्काला

object Lib2:
 import Lib1.*

 trait LastBlock[A]:
 def lastBlock(instance: A): Block

 given ethereumLastBlock:LastBlock[Ethereum] = new LastBlock[Ethereum]:
 def lastBlock(eth: Ethereum) = eth.lastBlock

 given bitcoinLastBlock:LastBlock[Bitcoin] = new LastBlock[Bitcoin]:
 def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")

import Lib1.*, Lib2.*

def useLastBlock[A](instance: A)(using behavior: LastBlock[A]) =
 behavior.lastBlock(instance)

println(useLastBlock(Ethereum(lastBlock = 2)))
println(useLastBlock(Bitcoin()))

पंक्ति 19 एक नया व्यवहार `LastBlock` परिभाषित किया गया है, ठीक वैसे ही जैसे हमने पहले किया था।

पंक्ति 22 और पंक्ति 25, `val` को ` द्वारा प्रतिस्थापित किया जाता हैgiven`. `के दोनों कार्यान्वयनLastBlock` को अंतर्निहित दायरे में रखा गया है।

पंक्ति 31`useLastBlock`व्यवहार की घोषणा करता है`LastBlock' एक अंतर्निहित पैरामीटर के रूप में। कंपाइलर ` के उचित उदाहरण का समाधान करता हैLastBlock`कॉलर स्थानों से प्रासंगिक अंतर्निहित दायरे से (पंक्तियाँ 33 और 34)। लाइन 28 ` से सब कुछ आयात करती हैLib2`, अंतर्निहित दायरे सहित। इसलिए, कंपाइलर परिभाषित पंक्तियों 22 और 25 को 'के अंतिम पैरामीटर के रूप में पास करता हैuseLastBlock`. 

एक पुस्तकालय उपयोगकर्ता के रूप में, टाइप क्लास का उपयोग करना पहले की तुलना में आसान है। लाइन 34 और 35 एक डेवलपर को केवल यह सुनिश्चित करना है कि व्यवहार का एक उदाहरण अंतर्निहित दायरे में इंजेक्ट किया गया है (और यह मात्र ` हो सकता हैimport`). यदि कोई अन्तर्निहित `नहीं हैgiven`कोड कहां है`using'यह, संकलक उसे बताता है।

स्काला के अंतर्निहित व्यवहार के उदाहरणों के साथ-साथ वर्ग के उदाहरणों को पारित करने का कार्य आसान हो गया है।

अंतर्निहित शर्करा

पिछले कोड की पंक्ति 22 और 25 को और बेहतर बनाया जा सकता है! आइए टीसी कार्यान्वयन पर दोबारा गौर करें।

स्काला

given LastBlock[Ethereum] = new LastBlock[Ethereum]:
 def lastBlock(eth: Ethereum) = eth.lastBlock

 given LastBlock[Bitcoin] = new LastBlock[Bitcoin]:
 def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")

पंक्ति 22 और 25, यदि उदाहरण का नाम अप्रयुक्त है, तो इसे छोड़ा जा सकता है।

स्काला


 given LastBlock[Ethereum] with
 def lastBlock(eth: Ethereum) = eth.lastBlock

 given LastBlock[Bitcoin] with
 def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")

पंक्ति 22 और 25, प्रकार की पुनरावृत्ति को ` से बदला जा सकता हैwith`कीवर्ड.

स्काला

given LastBlock[Ethereum] = _.lastBlock

 given LastBlock[Bitcoin] = _ => http("http://bitcoin/last")

क्योंकि हम इसमें एकल फ़ंक्शन के साथ एक विकृत विशेषता का उपयोग करते हैं, आईडीई एसएएम अभिव्यक्ति के साथ कोड को सरल बनाने का सुझाव दे सकता है। हालाँकि यह सही है, मुझे नहीं लगता कि यह एसएएम का उचित उपयोग है, जब तक कि आप लापरवाही से कोड गोल्फिंग नहीं कर रहे हों।

स्काला अनावश्यक नामकरण, घोषणा और प्रकार अतिरेक को हटाकर वाक्यविन्यास को सुव्यवस्थित करने के लिए वाक्यात्मक शर्करा प्रदान करता है।

विस्तार

बुद्धिमानी से उपयोग किया गया, `extension`तंत्र एक प्रकार के वर्ग का उपयोग करने के लिए वाक्यविन्यास को सरल बना सकता है।

स्काला

object Lib2:
 import Lib1.*

 trait LastBlock[A]:
 def lastBlock(instance: A): Block

 given LastBlock[Ethereum] with
 def lastBlock(eth: Ethereum) = eth.lastBlock

 given LastBlock[Bitcoin] with
 def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")

 extension[A](instance: A)
 def lastBlock(using tc: LastBlock[A]) = tc.lastBlock(instance)

import Lib1.*, Lib2.*

println(Ethereum(lastBlock = 2).lastBlock)
println(Bitcoin().lastBlock)

पंक्तियाँ 28-29 एक सामान्य विस्तार विधि `lastBlock`किसी भी `के लिए परिभाषित किया गया हैA` एक ` के साथLastBlock`अंतर्निहित दायरे में टीसी पैरामीटर।

लाइन 33-34 एक्सटेंशन टीसी का उपयोग करने के लिए ऑब्जेक्ट ओरिएंटेड सिंटैक्स का लाभ उठाता है।

स्काला

object Lib2:
 import Lib1.*

 trait LastBlock[A]:
 def lastBlock(instance: A): Block

 given LastBlock[Ethereum] with
 def lastBlock(eth: Ethereum) = eth.lastBlock

 given LastBlock[Bitcoin] with
 def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")

 extension[A](instance: A)(using tc: LastBlock[A])
 def lastBlock = tc.lastBlock(instance)
 def penultimateBlock = tc.lastBlock(instance) - 1

import Lib1.*, Lib2.*

val eth = Ethereum(lastBlock = 2)
println(eth.lastBlock)
println(eth.penultimateBlock)

val btc = Bitcoin()
println(btc.lastBlock)
println(btc.penultimateBlock)

पंक्ति 28, दोहराव से बचने के लिए टीसी पैरामीटर को पूरे एक्सटेंशन के लिए भी परिभाषित किया जा सकता है। पंक्ति 30 हम परिभाषित करने के लिए एक्सटेंशन में टीसी का पुन: उपयोग करते हैंpenultimateBlock` (भले ही इसे `पर लागू किया जा सकता हैLastBlock`विशेषता सीधे)

जादू तब होता है जब टीसी का उपयोग किया जाता है। अभिव्यक्ति बहुत अधिक स्वाभाविक लगती है, जिससे यह भ्रम होता है कि व्यवहार `lastBlock` उदाहरण के साथ मिला हुआ है।

टीसी के साथ सामान्य प्रकार
स्काला

import Lib1.*, Lib2.*

def useLastBlock1[A](instance: A)(using LastBlock[A]) = instance.lastBlock

def useLastBlock2[A: LastBlock](instance: A) = instance.lastBlock

val eth = Ethereum(lastBlock = 2)
assert(useLastBlock1(eth) == useLastBlock2(eth))

पंक्ति 34 फ़ंक्शन एक अंतर्निहित टीसी का उपयोग करता है। ध्यान दें कि यदि टीसी का नाम अनावश्यक है तो उसे नाम देने की आवश्यकता नहीं है।

टीसी पैटर्न इतना व्यापक रूप से उपयोग किया जाता है कि "अंतर्निहित व्यवहार के साथ एक प्रकार" को व्यक्त करने के लिए एक सामान्य प्रकार का वाक्यविन्यास होता है। पंक्ति 36 का वाक्यविन्यास पिछले वाले (पंक्ति 34) का अधिक संक्षिप्त विकल्प है। यह विशेष रूप से अनाम अंतर्निहित टीसी पैरामीटर घोषित करने से बचता है।

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

स्वचालित व्युत्पत्ति

बहुत सारी स्काला लाइब्रेरीज़ टीसी का उपयोग करती हैं, जिससे प्रोग्रामर को उन्हें अपने कोड बेस में लागू करना पड़ता है।

उदाहरण के लिए, Sirce (एक json डी-सीरियलाइज़ेशन लाइब्रेरी) TC का उपयोग करता हैEncoder[T]` और `Decoder[T]`प्रोग्रामर्स को अपने कोडबेस में लागू करने के लिए। एक बार लागू होने के बाद लाइब्रेरी के पूरे दायरे का उपयोग किया जा सकता है। 

टीसी के वे कार्यान्वयन अक्सर से अधिक होते हैं डेटा उन्मुख मैपर्स. उन्हें किसी व्यावसायिक तर्क की आवश्यकता नहीं है, लिखना उबाऊ है, और केस वर्गों के साथ तालमेल बनाए रखना एक बोझ है।

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

हुड के तहत, संकलन समय पर, सामान्य मैक्रोज़ आत्मनिरीक्षण करते हैं प्रकार शुद्ध डेटा संरचना के रूप में और पुस्तकालय उपयोगकर्ताओं के लिए टीसी[टी] उत्पन्न करता है। 

सामान्य तौर पर टीसी प्राप्त करना बहुत आम है, इसलिए स्काला ने उस उद्देश्य के लिए एक संपूर्ण टूल बॉक्स पेश किया। इस पद्धति का हमेशा लाइब्रेरी दस्तावेज़ों द्वारा विज्ञापन नहीं किया जाता है, हालाँकि यह व्युत्पत्ति का उपयोग करने का स्काला 3 तरीका है।

स्काला

object GenericLib:

 trait Named[A]:
 def blockchainName(instance: A): String

 object Named:
 import scala.deriving.*

 inline final def derived[A](using inline m: Mirror.Of[A]): Named[A] =
 val nameOfType: String = inline m match
 case p: Mirror.ProductOf[A] => compiletime.constValue[p.MirroredLabel]
 case _ => compiletime.error("Not a product")
 new Named[A]:
 override def blockchainName(instance: A):String = nameOfType.toLowerCase

 extension[A] (instance: A)(using tc: Named[A])
 def blockchainName = tc.blockchainName(instance)

import Lib1.*, GenericLib.*

case class Polkadot() derives Named
given Named[Bitcoin] = Named.derived
given Named[Ethereum] = Named.derived

println(Ethereum(lastBlock = 2).blockchainName)
println(Bitcoin().blockchainName)
println(Polkadot().blockchainName)

लाइन 18 एक नया टीसी`Named' का परिचय दिया गया है। स्पष्ट रूप से कहें तो यह टीसी ब्लॉकचेन व्यवसाय से असंबंधित है। इसका उद्देश्य केस क्लास के नाम के आधार पर ब्लॉकचेन का नाम देना है।

सबसे पहले परिभाषा पंक्ति 36-38 पर ध्यान दें। टीसी प्राप्त करने के लिए 2 वाक्यविन्यास हैं:

  1. लाइन 36 टीसी इंस्टेंस को सीधे केस क्लास पर `के साथ परिभाषित किया जा सकता हैderives`कीवर्ड. हुड के तहत कंपाइलर एक दिया गया `उत्पन्न करता हैNamed`में उदाहरण `Polkadot`सहचर वस्तु.
  2. पंक्ति 37 और 38, प्रकार वर्ग के उदाहरण पहले से मौजूद वर्गों पर `के साथ दिए गए हैंTC.derived

पंक्ति 31 में एक सामान्य विस्तार परिभाषित किया गया है (पिछले अनुभाग देखें) और `blockchainName` का प्रयोग प्राकृतिक रूप से किया जाता है।  

`derives`कीवर्ड `फॉर्म के साथ एक विधि की अपेक्षा करता हैinline def derived[T](using Mirror.Of[T]): TC[T] = ???`जो परिभाषित पंक्ति 24 है। मैं गहराई से नहीं बताऊंगा कि कोड क्या करता है। व्यापक रूपरेखा में:

  • `inline def` एक मैक्रो को परिभाषित करता है
  • `Mirror`प्रकारों का आत्मनिरीक्षण करने के लिए टूलबॉक्स का हिस्सा है। दर्पण विभिन्न प्रकार के होते हैं, और पंक्ति 26 का कोड 'पर केंद्रित हैProduct` दर्पण (एक केस क्लास एक उत्पाद है)। पंक्ति 27, यदि प्रोग्रामर कुछ ऐसा प्राप्त करने का प्रयास करते हैं जो 'नहीं' हैProduct`, कोड संकलित नहीं होगा।
  • `Mirror` में अन्य प्रकार शामिल हैं। उनमें से एक, `MirrorLabel`, एक स्ट्रिंग है जिसमें प्रकार का नाम होता है। इस मान का उपयोग `के कार्यान्वयन, पंक्ति 29 में किया जाता हैNamed`टीसी.

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

चाहे आपको टीसी लागू करने के लिए सामान्य या विशिष्ट कोड की आवश्यकता हो, प्रत्येक स्थिति के लिए एक समाधान है। 

सभी लाभों का सारांश

  • यह अभिव्यक्ति की समस्या का समाधान करता है
    • नए प्रकार पारंपरिक लक्षण विरासत के माध्यम से मौजूदा व्यवहार को लागू कर सकते हैं
    • नए व्यवहार मौजूदा प्रकारों पर लागू किए जा सकते हैं
  • चिंता का पृथक्करण
    • कोड उलझा हुआ नहीं है और आसानी से हटाया जा सकता है। एक टीसी डेटा और व्यवहार को अलग करती है, जो एक कार्यात्मक प्रोग्रामिंग आदर्श वाक्य है।
  • यह सुरक्षित है
    • यह सुरक्षित है क्योंकि यह आत्मनिरीक्षण पर निर्भर नहीं करता है। यह प्रकारों से जुड़े बड़े पैटर्न मिलान से बचता है। यदि आप खुद को ऐसा कोड लिखते हुए पाते हैं, तो आप एक ऐसे मामले का पता लगा सकते हैं जहां टीसी पैटर्न बिल्कुल उपयुक्त होगा।
    • अंतर्निहित तंत्र संकलन सुरक्षित है! यदि संकलन के समय कोई उदाहरण गायब है तो कोड संकलित नहीं होगा। रनटाइम पर कोई आश्चर्य नहीं.
  • यह तदर्थ बहुरूपता लाता है
    • पारंपरिक ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग में तदर्थ बहुरूपता आमतौर पर गायब होती है।
    • तदर्थ बहुरूपता के साथ, डेवलपर्स पारंपरिक उप टाइपिंग (जो कोड को जोड़ता है) का उपयोग किए बिना विभिन्न असंबंधित प्रकारों के लिए समान व्यवहार लागू कर सकते हैं।
  • निर्भरता इंजेक्शन को आसान बनाया गया
    • लिस्कोव प्रतिस्थापन सिद्धांत के संबंध में एक टीसी उदाहरण को बदला जा सकता है। 
    • जब किसी घटक की टीसी पर निर्भरता होती है, तो एक नकली टीसी को परीक्षण उद्देश्यों के लिए आसानी से इंजेक्ट किया जा सकता है। 

काउंटर संकेत

प्रत्येक हथौड़ा विभिन्न प्रकार की समस्याओं के लिए डिज़ाइन किया गया है।

प्रकार की कक्षाएं व्यवहार संबंधी समस्याओं के लिए हैं और इनका उपयोग डेटा इनहेरिटेंस के लिए नहीं किया जाना चाहिए। उस उद्देश्य के लिए रचना का प्रयोग करें.

सामान्य उपटाइपिंग अधिक सरल है. यदि आपके पास कोड आधार है और विस्तारशीलता का लक्ष्य नहीं है, तो प्रकार की कक्षाएं अत्यधिक हो सकती हैं।

उदाहरण के लिए, स्काला कोर में, एक ` हैNumeric`प्रकार वर्ग:

स्काला

trait Numeric[T] extends Ordering[T] {
 def plus(x: T, y: T): T
 def minus(x: T, y: T): T
 def times(x: T, y: T): T

इस प्रकार के वर्ग का उपयोग करना वास्तव में समझ में आता है क्योंकि यह न केवल स्काला (इंट, बिगइंट, ...) में एम्बेडेड प्रकारों पर बीजगणितीय एल्गोरिदम के पुन: उपयोग की अनुमति देता है, बल्कि उपयोगकर्ता द्वारा परिभाषित प्रकारों (ए `) पर भीComplexNumber`उदाहरण के लिए)।

दूसरी ओर, स्काला संग्रह के कार्यान्वयन में अधिकतर प्रकार वर्ग के बजाय उपप्रकार का उपयोग किया जाता है। यह डिज़ाइन कई कारणों से समझ में आता है:

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

निष्कर्ष

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

आशा है कि आपको इस दस्तावेज़ को पढ़कर ज्ञान प्राप्त हुआ होगा। 

कोड यहां उपलब्ध है https://github.com/jprudent/type-class-article. यदि आपके पास किसी भी प्रकार का प्रश्न या टिप्पणी है तो कृपया मुझसे संपर्क करें। यदि आप चाहें तो आप रिपॉजिटरी में मुद्दों या कोड टिप्पणियों का उपयोग कर सकते हैं।


जेरोम प्रूडेंट

सॉफ्टवेयर इंजीनियर

समय टिकट:

से अधिक खाता