Scala3 میں کلاسز ٹائپ کریں: ایک ابتدائی رہنما | لیجر

Scala3 میں کلاسز ٹائپ کریں: ایک ابتدائی رہنما | لیجر

Type Classes in Scala3: A Beginner's Guide | Ledger PlatoBlockchain Data Intelligence. Vertical Search. Ai.

اس دستاویز کا مقصد ابتدائی Scala3 ڈویلپر کے لیے ہے جو پہلے سے ہی Scala نثر میں مہارت رکھتا ہے، لیکن تمام ` کے بارے میں حیران ہے۔implicits` اور کوڈ میں پیرامیٹرائزڈ خصوصیات۔

یہ دستاویز وضاحت کرتی ہے کہ کیوں، کیسے، کہاں اور کب قسم کی کلاسز (TC).

اس دستاویز کو پڑھنے کے بعد ابتدائی 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 مشکل ہے۔ ہم نے اسے پولیمورفزم کی اپنی تعریف اور 'توسیع' چال کے ساتھ نافذ کرنے کی کوشش کی۔ اور یہ عجیب تھا۔

ایک گمشدہ ٹکڑا ہے جسے کہتے ہیں۔ ایڈہاک پولیمورفزم: ایک قسم کے مطابق رویے کے نفاذ کو محفوظ طریقے سے بھیجنے کی صلاحیت، جہاں بھی طرز عمل اور قسم کی تعریف کی گئی ہو۔ درج کریں۔ کلاس ٹائپ کریں۔ پیٹرن ہے

ٹائپ کلاس پیٹرن

ٹائپ کلاس (مختصر کے لیے TC) پیٹرن کی ترکیب میں 3 مراحل ہیں۔ 

  1. ایک نئے طرز عمل کی وضاحت کریں۔
  2. طرز عمل کو نافذ کریں۔
  3. طرز عمل کا استعمال کریں۔

مندرجہ ذیل حصے میں، میں TC پیٹرن کو انتہائی سیدھے طریقے سے لاگو کرتا ہوں۔ یہ لفظی، clunky اور ناقابل عمل ہے. لیکن ٹھہرو، وہ انتباہات کو دستاویز میں قدم بہ قدم طے کیا جائے گا۔

1. ایک نئے رویے کی وضاحت کریں۔
بڑے پیمانے پر

object Lib2:
 import Lib1.*

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

`Lib1` ایک بار پھر، اچھوتا چھوڑ دیا گیا ہے۔

نیا سلوک is ٹی سی کو خصلت کے ذریعے عملی شکل دی گئی۔ خصوصیت میں بیان کردہ افعال اس طرز عمل کے کچھ پہلوؤں کو لاگو کرنے کا ایک طریقہ ہیں۔

پیرامیٹر `A` اس قسم کی نمائندگی کرتا ہے جس پر ہم رویے کا اطلاق کرنا چاہتے ہیں، جو ` کی ذیلی قسمیں ہیں۔Blockchainہمارے معاملے میں۔

کچھ ریمارکس:

  • اگر ضرورت ہو تو، پیرامیٹرائزڈ قسم `A` اسکالا قسم کے نظام کی طرف سے مزید محدود کیا جا سکتا ہے. مثال کے طور پر، ہم نافذ کر سکتے ہیں۔A` ایک ` ہوناBlockchain`. 
  • اس کے علاوہ، TC میں اس میں اعلان کردہ اور بھی بہت سے افعال ہو سکتے ہیں۔
  • آخر میں، ہر فنکشن میں کئی اور صوابدیدی پیرامیٹرز ہو سکتے ہیں۔

لیکن آئیے پڑھنے کی قابلیت کی خاطر چیزوں کو سادہ رکھیں۔

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 ایک غیر منظم IO کے ساتھ لاگو کیا گیا ہے اور اس کا پیرامیٹر استعمال نہیں کرتا ہے۔

تو، `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` نمائندگی (اصل A) اور اس کے رویے کے درمیان گلو ہے۔ ڈیٹا اور رویے کو الگ کر دیا گیا ہے، جس کے لیے فنکشنل پروگرامنگ کی وکالت کرتی ہے۔

بحث

آئیے اظہار کے مسئلے کے قواعد کو دوبارہ دیکھیں:

  • اصول 1: کے نفاذ کی اجازت دیں۔ موجودہ طرز عمل  پر لاگو کیا جائے نئی کلاسز
  • اصول 2: کے نفاذ کی اجازت دیں۔ نئے طرز عمل پر لاگو کیا جائے موجودہ کلاسز
  • قاعدہ 3: اسے خطرے میں نہیں ڈالنا چاہئے۔ قسم کی حفاظت
  • قاعدہ 4: اسے دوبارہ مرتب کرنے کی ضرورت نہیں ہونی چاہئے۔ موجودہ کوڈ

اصول 1 کو ذیلی قسم کے پولیمورفزم کے ساتھ باکس سے باہر حل کیا جا سکتا ہے۔

ابھی پیش کردہ TC پیٹرن (پچھلا اسکرین شاٹ دیکھیں) اصول 2 کو حل کرتا ہے۔Lib1` (قاعدہ 4)۔ 

تاہم کئی وجوہات کی بنا پر استعمال کرنا غیر عملی ہے:

  • لائنز 33-34 ہمیں اس کی مثال کے ساتھ رویے کو واضح طور پر منتقل کرنا ہوگا۔ یہ ایک اضافی اوور ہیڈ ہے۔ ہمیں صرف لکھنا چاہئے۔useLastBlock(Bitcoin())`.
  • لائن 31 نحو غیر معمولی ہے۔ ہم اس کے بجائے ایک مختصر اور زیادہ آبجیکٹ پر مبنی لکھنے کو ترجیح دیں گے۔instance.lastBlock()بیان

آئیے TC کے عملی استعمال کے لیے اسکیلا کی کچھ خصوصیات کو اجاگر کرتے ہیں۔ 

بہتر ڈویلپر کا تجربہ

Scala میں خصوصیات اور مصنوعی شکروں کا ایک انوکھا مجموعہ ہے جو TC کو ڈویلپرز کے لیے واقعی ایک خوشگوار تجربہ بناتا ہے۔

مضمرات

مضمر دائرہ ایک خاص دائرہ کار ہے جسے مرتب کرنے کے وقت حل کیا جاتا ہے جہاں دی گئی قسم کی صرف ایک مثال موجود ہو سکتی ہے۔ 

ایک پروگرام ` کے ساتھ مضمر دائرہ کار میں ایک مثال رکھتا ہے۔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")

چونکہ ہم اس میں ایک فنکشن کے ساتھ ایک انحطاط شدہ خصوصیت کا استعمال کرتے ہیں، IDE SAM اظہار کے ساتھ کوڈ کو آسان بنانے کا مشورہ دے سکتا ہے۔ اگرچہ درست ہے، مجھے نہیں لگتا کہ یہ SAM کا صحیح استعمال ہے، جب تک کہ آپ اتفاق سے گولفنگ کو کوڈ نہ کر لیں۔

اسکالا نحو کو ہموار کرنے کے لیے نحوی شکر پیش کرتا ہے، غیر ضروری نام دینے، اعلانیہ اور قسم کی فالتو پن کو ہٹاتا ہے۔

توسیع

سمجھداری سے استعمال کیا گیا، `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` TC پیرامیٹر مضمر دائرہ کار میں۔

لائنز 33-34 ایکسٹینشن TC استعمال کرنے کے لیے ایک آبجیکٹ اورینٹڈ نحو کا فائدہ اٹھاتی ہے۔

بڑے پیمانے پر

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، TC پیرامیٹر کی وضاحت بھی پوری توسیع کے لیے کی جا سکتی ہے تاکہ تکرار سے بچا جا سکے۔ لائن 30 ہم ` کی وضاحت کے لیے ایکسٹینشن میں ٹی سی کو دوبارہ استعمال کرتے ہیں۔penultimateBlock` (اگرچہ یہ ` پر لاگو کیا جا سکتا ہےLastBlock` خصوصیت براہ راست)

جادو تب ہوتا ہے جب TC استعمال ہوتا ہے۔ اظہار بہت زیادہ قدرتی محسوس ہوتا ہے، اس رویے کو وہم دیتا ہے۔lastBlock` مثال کے ساتھ ملا ہوا ہے۔

TC کے ساتھ عام قسم
بڑے پیمانے پر

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 فنکشن ایک مضمر TC استعمال کرتا ہے۔ نوٹ کریں کہ اگر یہ نام غیر ضروری ہے تو TC کو نام دینے کی ضرورت نہیں ہے۔

TC پیٹرن اس قدر وسیع پیمانے پر استعمال کیا جاتا ہے کہ ایک عام قسم کا نحو موجود ہے جس میں "مضمون سلوک کے ساتھ ایک قسم" کا اظہار کیا جاتا ہے۔ سطر 36 نحو پچھلے ایک (لائن 34) کا زیادہ جامع متبادل ہے۔ یہ خاص طور پر بے نام مضمر TC پیرامیٹر کا اعلان کرنے سے گریز کرتا ہے۔

یہ ڈویلپر کے تجربے کے سیکشن کو ختم کرتا ہے۔ ہم نے دیکھا ہے کہ جب TC کا استعمال کیا جاتا ہے اور اس کی تعریف کی جاتی ہے تو ایکسٹینشنز، مضمرات اور کچھ نحوی چینی کس طرح کم بے ترتیبی والا نحو فراہم کر سکتے ہیں۔

خودکار اخذ

بہت ساری اسکیلا لائبریریاں TC کا استعمال کرتی ہیں، پروگرامر کو ان کے کوڈ بیس میں لاگو کرنے کے لیے چھوڑ دیتے ہیں۔

مثال کے طور پر سرس (ایک json ڈی سیریلائزیشن لائبریری) TC ` کا استعمال کرتا ہے۔Encoder[T]` اور `Decoder[T]پروگرامرز کو اپنے کوڈ بیس میں لاگو کرنے کے لیے۔ ایک بار لاگو ہونے کے بعد لائبریری کا پورا دائرہ کار استعمال کیا جا سکتا ہے۔ 

TC کے وہ نفاذ اکثر سے زیادہ ہوتے ہیں۔ ڈیٹا پر مبنی میپرز. انہیں کسی کاروباری منطق کی ضرورت نہیں ہے، لکھنے میں بورنگ ہیں، اور کیس کلاسز کے ساتھ ہم آہنگی برقرار رکھنے کے لیے ایک بوجھ ہے۔

ایسی صورت حال میں وہ کتب خانے پیش کرتے ہیں جسے کہتے ہیں۔ خود کار طریقے سے اخذ یا نیم خود کار طریقے سے اخذ مثال کے طور پر سرس کو دیکھیں خود کار طریقے سے اور نیم خود کار طریقے سے اخذ نیم خودکار اخذ کے ساتھ پروگرامر کچھ معمولی نحو کے ساتھ ایک قسم کی کلاس کی مثال کا اعلان کر سکتا ہے، جبکہ خودکار اخذ کرنے کے لیے درآمد کے علاوہ کسی کوڈ میں ترمیم کی ضرورت نہیں ہے۔

ہڈ کے نیچے، مرتب کرنے کے وقت، عام میکرو خود کا معائنہ کرتے ہیں۔ اقسام خالص ڈیٹا سٹرکچر کے طور پر اور لائبریری صارفین کے لیے TC[T] تیار کریں۔ 

عام طور پر TC حاصل کرنا بہت عام ہے، اس لیے Scala نے اس مقصد کے لیے ایک مکمل ٹول باکس متعارف کرایا۔ اس طریقہ کی ہمیشہ لائبریری دستاویزات کے ذریعہ تشہیر نہیں کی جاتی ہے حالانکہ یہ اخذ کرنے کا Scala 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 ایک نیا TC `Named` متعارف کرایا گیا ہے۔ یہ TC سختی سے بات کرتے ہوئے بلاکچین کاروبار سے غیر متعلق ہے۔ اس کا مقصد کیس کلاس کے نام کی بنیاد پر بلاک چین کا نام رکھنا ہے۔

سب سے پہلے تعریف کی لائنوں 36-38 پر توجہ دیں۔ TC اخذ کرنے کے لیے 2 نحو ہیں:

  1. لائن 36 TC مثال کی براہ راست کیس کلاس پر ` کے ساتھ تعریف کی جا سکتی ہے۔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ٹی سی

TC مصنفین ایسے فنکشنز فراہم کرنے کے لیے میٹا پروگرامنگ کا استعمال کر سکتے ہیں جو عام طور پر ایک قسم کے دیے گئے TC کی مثالیں پیدا کرتے ہیں۔ پروگرامرز اپنے کوڈ کے لیے مثالیں بنانے کے لیے وقف لائبریری API یا Scala اخذ کرنے والے ٹولز کا استعمال کر سکتے ہیں۔

چاہے آپ کو TC لاگو کرنے کے لیے عام یا مخصوص کوڈ کی ضرورت ہو، ہر صورت حال کے لیے ایک حل موجود ہے۔ 

تمام فوائد کا خلاصہ

  • یہ اظہار کا مسئلہ حل کرتا ہے۔
    • نئی اقسام روایتی خصلت وراثت کے ذریعے موجودہ طرز عمل کو نافذ کر سکتی ہیں۔
    • موجودہ اقسام پر نئے طرز عمل کو لاگو کیا جا سکتا ہے۔
  • تشویش کی علیحدگی
    • کوڈ گنگنا ہوا نہیں ہے اور آسانی سے ڈیلیٹ کیا جا سکتا ہے۔ ایک TC ڈیٹا اور رویے کو الگ کرتا ہے، جو کہ ایک فعال پروگرامنگ کا نعرہ ہے۔
  • یہ محفوظ ہے
    • یہ محفوظ قسم کی ہے کیونکہ یہ خود شناسی پر انحصار نہیں کرتا ہے۔ یہ قسموں پر مشتمل بڑے پیٹرن کے ملاپ سے گریز کرتا ہے۔ اگر آپ خود کو اس طرح کا کوڈ لکھتے ہوئے محسوس کرتے ہیں، تو آپ کو ایک ایسے کیس کا پتہ چل سکتا ہے جہاں TC پیٹرن بالکل موزوں ہوگا۔
    • مضمر طریقہ کار مرتب محفوظ ہے! اگر کمپائل کے وقت کوئی مثال غائب ہے تو کوڈ مرتب نہیں ہوگا۔ رن ٹائم پر کوئی تعجب نہیں.
  • یہ ایڈہاک پولیمورفزم لاتا ہے۔
    • روایتی آبجیکٹ اورینٹڈ پروگرامنگ میں عام طور پر ایڈہاک پولیمورفزم غائب ہے۔
    • ایڈہاک پولیمورفزم کے ساتھ، ڈویلپرز روایتی ذیلی ٹائپنگ کا استعمال کیے بغیر مختلف غیر متعلقہ اقسام کے لیے ایک ہی طرز عمل کو نافذ کر سکتے ہیں (جو کوڈ کو جوڑتا ہے)
  • انحصار انجکشن آسان بنا دیا
    • Liskov متبادل اصول کے سلسلے میں TC مثال کو تبدیل کیا جا سکتا ہے۔ 
    • جب کسی جزو کا TC پر انحصار ہوتا ہے، تو ایک طنزیہ TC آسانی سے جانچ کے مقاصد کے لیے انجکشن لگایا جا سکتا ہے۔ 

جوابی اشارے

ہر ہتھوڑا مسائل کی ایک حد کے لئے ڈیزائن کیا گیا ہے.

قسم کی کلاسیں رویے کے مسائل کے لیے ہیں اور ڈیٹا وراثت کے لیے استعمال نہیں ہونا چاہیے۔ اس مقصد کے لیے ترکیب استعمال کریں۔

معمول کی ذیلی ٹائپنگ زیادہ سیدھی ہے۔ اگر آپ کوڈ بیس کے مالک ہیں اور آپ توسیع پذیری کا مقصد نہیں رکھتے ہیں، تو قسم کی کلاسیں حد سے زیادہ ہو سکتی ہیں۔

مثال کے طور پر، Scala کور میں، ایک ` ہے۔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

اس قسم کی کلاس کا استعمال کرنا واقعی سمجھ میں آتا ہے کیونکہ یہ نہ صرف اسکالا (Int, BigInt, …) میں سرایت شدہ اقسام پر الجبری الگورتھم کے دوبارہ استعمال کی اجازت دیتا ہے بلکہ صارف کی وضاحت شدہ اقسام پر بھی (a `ComplexNumberمثال کے طور پر)۔

دوسری طرف، Scala مجموعہ کے نفاذ میں زیادہ تر ٹائپ کلاس کے بجائے ذیلی ٹائپنگ کا استعمال کیا جاتا ہے۔ یہ ڈیزائن کئی وجوہات کی بناء پر معنی رکھتا ہے:

  • مجموعہ API کو مکمل اور مستحکم سمجھا جاتا ہے۔ یہ عمل درآمد سے وراثت میں ملنے والی خصلتوں کے ذریعے عام رویے کو بے نقاب کرتا ہے۔ انتہائی قابل توسیع ہونا یہاں کوئی خاص مقصد نہیں ہے۔
  • اسے استعمال میں آسان ہونا چاہیے۔ TC اختتامی صارف پروگرامر پر ذہنی اوور ہیڈ کا اضافہ کرتا ہے۔
  • TC کو کارکردگی میں چھوٹا اوور ہیڈ بھی لگ سکتا ہے۔ یہ مجموعہ API کے لیے اہم ہو سکتا ہے۔
  • اگرچہ، مجموعہ API تیسرے فریق کی لائبریریوں کے ذریعے بیان کردہ نئے TC کے ذریعے اب بھی قابل توسیع ہے۔

نتیجہ

ہم نے دیکھا ہے کہ TC ایک سادہ نمونہ ہے جو ایک بڑا مسئلہ حل کرتا ہے۔ Scala امیر نحو کی بدولت، TC پیٹرن کو کئی طریقوں سے لاگو اور استعمال کیا جا سکتا ہے۔ ٹی سی پیٹرن فنکشنل پروگرامنگ پیراڈائم کے مطابق ہے اور صاف فن تعمیر کے لیے ایک شاندار ٹول ہے۔ کوئی سلور بلٹ نہیں ہے اور جب یہ فٹ ہو جائے تو TC پیٹرن کا اطلاق کرنا ضروری ہے۔

امید ہے کہ آپ نے اس دستاویز کو پڑھ کر علم حاصل کیا ہوگا۔ 

کوڈ پر دستیاب ہے۔ https://github.com/jprudent/type-class-article. اگر آپ کے پاس کسی قسم کے سوالات یا ریمارکس ہیں تو براہ کرم مجھ سے رابطہ کریں۔ اگر آپ چاہیں تو آپ ذخیرے میں مسائل یا کوڈ کے تبصرے استعمال کرسکتے ہیں۔


جیروم پروڈنٹ

سافٹ ویئر انجنیئر

ٹائم اسٹیمپ:

سے زیادہ لیجر