Scala3-তে ক্লাস টাইপ করুন: একটি শিক্ষানবিস গাইড | খাতা

Scala3-তে ক্লাস টাইপ করুন: একটি শিক্ষানবিস গাইড | খাতা

উত্স নোড: 3028113

এই নথিটি শিক্ষানবিস Scala3 বিকাশকারীর জন্য তৈরি করা হয়েছে যিনি ইতিমধ্যেই স্কালা গদ্যে পারদর্শী, কিন্তু সমস্ত `সম্পর্কে বিভ্রান্তimplicits` এবং কোডে প্যারামিটারাইজড বৈশিষ্ট্য।

এই নথিটি ব্যাখ্যা করে কেন, কীভাবে, কোথায় এবং কখন টাইপ ক্লাস (TC).

এই নথিটি পড়ার পরে শিক্ষানবিস Scala3 বিকাশকারী এর সোর্স কোড ব্যবহার করতে এবং ডুব দেওয়ার জন্য কঠিন জ্ঞান অর্জন করবে অনেক স্কালা লাইব্রেরি থেকে এবং ইডিওমেটিক স্কালা কোড লেখা শুরু করুন।

চলুন শুরু করা যাক কেন…

অভিব্যক্তি সমস্যা

1998 সালে ফিলিপ ওয়াডলার জানিয়েছেন যে "অভিব্যক্তি সমস্যা একটি পুরানো সমস্যার একটি নতুন নাম"। এটি সফ্টওয়্যার এক্সটেনসিবিলিটির সমস্যা। মিস্টার ওয়াডলারের লেখার মতে, অভিব্যক্তি সমস্যার সমাধানের জন্য নিম্নলিখিত নিয়মগুলি মেনে চলতে হবে:

  • নিয়ম 1: এর বাস্তবায়নের অনুমতি দিন বিদ্যমান আচরণ (স্কালা বৈশিষ্ট্যের কথা চিন্তা করুন) প্রয়োগ করতে হবে নতুন উপস্থাপনা (একটি কেস ক্লাস চিন্তা করুন)
  • নিয়ম 2:  এর বাস্তবায়নের অনুমতি দিন নতুন আচরণ প্রয়োগ করা বিদ্যমান উপস্থাপনা
  • নিয়ম 3: এটা অবশ্যই বিপদে ফেলবে না সুরক্ষা টাইপ করুন
  • নিয়ম 4: এটি পুনরায় কম্পাইল করার প্রয়োজন হবে না বিদ্যমান কোড

এই সমস্যার সমাধান এই নিবন্ধের রূপালী থ্রেড হবে.

নিয়ম 1: নতুন প্রতিনিধিত্বে বিদ্যমান আচরণের বাস্তবায়ন

যেকোন অবজেক্ট ওরিয়েন্টেড ল্যাঙ্গুয়েজ এর সাথে নিয়ম 1 এর জন্য একটি বেকড-ইন সমাধান রয়েছে সাবটাইপ পলিমরফিজম. আপনি নিরাপদে যেকোনো ` বাস্তবায়ন করতে পারেনtrait` a এর উপর নির্ভরশীলতায় সংজ্ঞায়িতclassনির্ভরতা পুনরায় কম্পাইল না করে আপনার নিজের কোডে। চলুন এটি কর্মে দেখি:

scala

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`.

প্রথম জিনিস যা মনে আসে তা হল প্যারামিটারের ধরণের উপর ভিত্তি করে একটি বড় সুইচ তৈরি করা।

scala

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`.

scala

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 প্যাটার্ন বাস্তবায়ন করি। এটি শব্দসমৃদ্ধ, অবাস্তব এবং অবাস্তব। তবে ধরে রাখুন, নথিতে ধাপে ধাপে সেই সতর্কতাগুলি ঠিক করা হবে।

1. একটি নতুন আচরণ সংজ্ঞায়িত করুন
scala

object Lib2:
  import Lib1.*

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

`Lib1` আবার, অস্পৃশ্য বাকি আছে.

নতুন আচরণ is TC বৈশিষ্ট্য দ্বারা বাস্তবায়িত. বৈশিষ্ট্যে সংজ্ঞায়িত ফাংশনগুলি সেই আচরণের কিছু দিক প্রয়োগ করার একটি উপায়।

পরামিতি `A` যে ধরনের আচরণ আমরা প্রয়োগ করতে চাই তা উপস্থাপন করে, যা ` এর উপপ্রকারBlockchain`আমাদের ক্ষেত্রে।

কিছু মন্তব্য:

  • প্রয়োজন হলে, প্যারামিটারাইজড টাইপ `A` স্কালা টাইপ সিস্টেম দ্বারা আরও সীমাবদ্ধ করা যেতে পারে। উদাহরণস্বরূপ, আমরা প্রয়োগ করতে পারি `A` একটি ` হতেBlockchain`. 
  • এছাড়াও, TC এর মধ্যে আরও অনেক ফাংশন ঘোষিত হতে পারে।
  • অবশেষে, প্রতিটি ফাংশনে আরও অনেক নির্বিচারী পরামিতি থাকতে পারে।

তবে পঠনযোগ্যতার জন্য জিনিসগুলি সহজ রাখা যাক।

2. আচরণ বাস্তবায়ন
scala

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. আচরণ ব্যবহার করুন
scala

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 সমাধান করে। এটি টাইপ নিরাপদ (নিয়ম 3) এবং আমরা কখনও ` স্পর্শ করিনিLib1` (নিয়ম 4)। 

তবে এটি বিভিন্ন কারণে ব্যবহার করা অবাস্তব:

  • লাইন 33-34 আমাদের স্পষ্টভাবে তার উদাহরণ বরাবর আচরণ পাস করতে হবে। এটি একটি অতিরিক্ত ওভারহেড। আমাদের শুধু লিখতে হবে `useLastBlock(Bitcoin())`.
  • লাইন 31 সিনট্যাক্স অস্বাভাবিক। আমরা বরং একটি সংক্ষিপ্ত এবং আরও অবজেক্ট ওরিয়েন্টেড লিখতে পছন্দ করব `instance.lastBlock()` বিবৃতি।

ব্যবহারিক TC ব্যবহারের জন্য কিছু স্কালা বৈশিষ্ট্য হাইলাইট করা যাক। 

উন্নত বিকাশকারী অভিজ্ঞতা

স্কালার বৈশিষ্ট্য এবং সিনট্যাকটিক শর্করার একটি অনন্য সেট রয়েছে যা TC কে ডেভেলপারদের জন্য সত্যিই একটি উপভোগ্য অভিজ্ঞতা করে তোলে।

অন্তর্নিহিত

অন্তর্নিহিত সুযোগ হল একটি বিশেষ সুযোগ যা কম্পাইলের সময়ে সমাধান করা হয় যেখানে একটি প্রদত্ত প্রকারের শুধুমাত্র একটি উদাহরণ থাকতে পারে। 

একটি প্রোগ্রাম `এর সাথে অন্তর্নিহিত সুযোগে একটি উদাহরণ রাখেgiven` কীওয়ার্ড। বিকল্পভাবে একটি প্রোগ্রাম কিওয়ার্ড ` দিয়ে অন্তর্নিহিত সুযোগ থেকে একটি উদাহরণ পুনরুদ্ধার করতে পারেusing`.

অন্তর্নিহিত সুযোগ কম্পাইল সময়ে সমাধান করা হয়, রানটাইমে গতিশীলভাবে পরিবর্তন করার উপায় আছে। প্রোগ্রাম কম্পাইল হলে, অন্তর্নিহিত সুযোগ সমাধান করা হয়। রানটাইমে, যেখানে সেগুলি ব্যবহার করা হয় সেখানে অন্তর্নিহিত উদাহরণগুলি অনুপস্থিত থাকা সম্ভব নয়। শুধুমাত্র সম্ভাব্য বিভ্রান্তি ভুল অন্তর্নিহিত উদাহরণ ব্যবহার করে আসতে পারে, কিন্তু এই সমস্যাটি চেয়ার এবং কীবোর্ডের মধ্যে প্রাণীর জন্য ছেড়ে দেওয়া হয়।

এটি একটি বিশ্বব্যাপী সুযোগ থেকে ভিন্ন কারণ: 

  1. এটি প্রাসঙ্গিকভাবে সমাধান করা হয়েছে। একটি প্রোগ্রামের দুটি অবস্থান অন্তর্নিহিত সুযোগে একই প্রদত্ত ধরণের একটি উদাহরণ ব্যবহার করতে পারে, তবে সেই দুটি উদাহরণ ভিন্ন হতে পারে।
  2. দৃশ্যের আড়ালে কোডটি অন্তর্নিহিত আর্গুমেন্ট ফাংশন পাস করছে যতক্ষণ না অন্তর্নিহিত ব্যবহার না হয়। এটি একটি বিশ্বব্যাপী মেমরি স্থান ব্যবহার করছে না।

টাইপ ক্লাসে ফিরে যাচ্ছি! ঠিক একই উদাহরণ নেওয়া যাক।

scala

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` একই অপরিবর্তিত কোড যা আমরা পূর্বে সংজ্ঞায়িত করেছি। 

scala

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 লাইন আরও উন্নত করা যেতে পারে! আসুন টিসি বাস্তবায়নের পুনরাবৃত্তি করি।

scala

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, উদাহরণের নাম অব্যবহৃত হলে, এটি বাদ দেওয়া যেতে পারে।

scala


  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` কীওয়ার্ড।

scala

given LastBlock[Ethereum] = _.lastBlock

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

যেহেতু আমরা এটিতে একটি একক ফাংশন সহ একটি অবক্ষয়িত বৈশিষ্ট্য ব্যবহার করি, তাই IDE একটি SAM এক্সপ্রেশন দিয়ে কোডটিকে সরল করার পরামর্শ দিতে পারে। যদিও সঠিক, আমি মনে করি না এটি SAM-এর সঠিক ব্যবহার, যদি না আপনি স্বাভাবিকভাবে গলফিং কোড করেন।

অপ্রয়োজনীয় নামকরণ, ঘোষণা এবং টাইপ রিডানডেন্সি অপসারণ করে সিনট্যাক্সকে স্ট্রিমলাইন করার জন্য স্কালা সিনট্যাকটিক শর্করা অফার করে।

প্রসার

বুদ্ধিমানের সাথে ব্যবহার করা হয়েছে, `extension` মেকানিজম টাইপ ক্লাস ব্যবহার করার জন্য সিনট্যাক্সকে সহজ করতে পারে।

scala

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 এক্সটেনশন টিসি ব্যবহার করার জন্য একটি অবজেক্ট ওরিয়েন্টেড সিনট্যাক্স ব্যবহার করে।

scala

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 আমরা ` সংজ্ঞায়িত করার জন্য এক্সটেনশনে TC পুনরায় ব্যবহার করিpenultimateBlock` (যদিও এটি `তে প্রয়োগ করা যেতে পারেLastBlock` বৈশিষ্ট্য সরাসরি)

TC ব্যবহার করা হলে যাদু ঘটে। অভিব্যক্তিটি অনেক বেশি স্বাভাবিক বোধ করে, সেই আচরণকে বিভ্রম দেয় `lastBlock` উদাহরণের সাথে মিলিত হয়।

TC সহ জেনেরিক টাইপ
scala

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 ব্যবহার করে, প্রোগ্রামারকে তাদের কোড বেসে প্রয়োগ করার জন্য রেখে দেয়।

উদাহরণস্বরূপ Circe (একটি json ডি-সিরিয়ালাইজেশন লাইব্রেরি) TC ` ব্যবহার করেEncoder[T]` এবং `Decoder[T]' প্রোগ্রামারদের তাদের কোডবেসে বাস্তবায়নের জন্য। একবার বাস্তবায়িত হলে লাইব্রেরির পুরো সুযোগ ব্যবহার করা যেতে পারে। 

TC এর বাস্তবায়ন প্রায়ই বেশী হয় ডেটা ওরিয়েন্টেড ম্যাপার. তাদের কোন ব্যবসায়িক যুক্তির প্রয়োজন নেই, লিখতে বিরক্তিকর এবং কেস ক্লাসের সাথে সমন্বয় বজায় রাখার জন্য একটি বোঝা।

এমন পরিস্থিতিতে ওই লাইব্রেরিগুলো যাকে বলে স্বয়ংক্রিয় derivation or আধা স্বয়ংক্রিয় ডেরিভেশন উদাহরণস্বরূপ সার্স দেখুন স্বয়ংক্রিয় এবং আধা স্বয়ংক্রিয় ডেরিভেশন আধা-স্বয়ংক্রিয় ডেরাইভেশনের মাধ্যমে প্রোগ্রামার কিছু ছোট সিনট্যাক্স সহ একটি টাইপ ক্লাসের একটি উদাহরণ ঘোষণা করতে পারে, যেখানে স্বয়ংক্রিয় ডেরিভেশনের জন্য একটি আমদানি ব্যতীত কোনো কোড পরিবর্তনের প্রয়োজন হয় না।

হুড অধীনে, কম্পাইল সময়ে, জেনেরিক ম্যাক্রো অন্তর্নিদর্শন ধরনের বিশুদ্ধ ডেটা স্ট্রাকচার হিসেবে এবং লাইব্রেরি ব্যবহারকারীদের জন্য একটি TC[T] তৈরি করে। 

সাধারণভাবে একটি TC তৈরি করা খুবই সাধারণ, তাই স্কালা সেই উদ্দেশ্যে একটি সম্পূর্ণ টুল বক্স চালু করেছে। এই পদ্ধতিটি সর্বদা লাইব্রেরি ডকুমেন্টেশন দ্বারা বিজ্ঞাপন দেওয়া হয় না যদিও এটি ডেরিভেশন ব্যবহার করার স্কালা 3 উপায়।

scala

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 টিসি দৃষ্টান্ত সরাসরি কেস ক্লাসে ` দিয়ে সংজ্ঞায়িত করা যেতে পারে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 বা স্কালা ডেরাইভিং টুল ব্যবহার করতে পারে।

একটি TC বাস্তবায়নের জন্য আপনার জেনেরিক বা নির্দিষ্ট কোডের প্রয়োজন হোক না কেন, প্রতিটি পরিস্থিতির জন্য একটি সমাধান রয়েছে। 

সমস্ত সুবিধার সারসংক্ষেপ

  • এটি অভিব্যক্তি সমস্যা সমাধান করে
    • নতুন ধরনের ঐতিহ্যগত বৈশিষ্ট্য উত্তরাধিকার মাধ্যমে বিদ্যমান আচরণ বাস্তবায়ন করতে পারে
    • বিদ্যমান প্রকারের উপর নতুন আচরণ প্রয়োগ করা যেতে পারে
  • উদ্বেগের বিচ্ছেদ
    • কোডটি বিকৃত নয় এবং সহজেই মুছে ফেলা যায়। একটি টিসি ডেটা এবং আচরণকে আলাদা করে, যা একটি কার্যকরী প্রোগ্রামিং নীতিবাক্য।
  • এটা নিরাপদ
    • এটি টাইপ নিরাপদ কারণ এটি আত্মদর্শনের উপর নির্ভর করে না। এটা টাইপ জড়িত বড় প্যাটার্ন ম্যাচিং এড়ায়. আপনি যদি এই ধরনের কোড লেখার সম্মুখীন হন, আপনি এমন একটি কেস সনাক্ত করতে পারেন যেখানে TC প্যাটার্ন পুরোপুরি উপযুক্ত হবে।
    • অন্তর্নিহিত প্রক্রিয়া কম্পাইল নিরাপদ! কম্পাইলের সময় কোনো উদাহরণ অনুপস্থিত থাকলে কোডটি কম্পাইল হবে না। রানটাইমে কোন চমক নেই।
  • এটি অ্যাড-হক পলিমরফিজম নিয়ে আসে
    • প্রচলিত অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিংয়ে অ্যাডহক পলিমরফিজম সাধারণত অনুপস্থিত থাকে।
    • অ্যাড-হক পলিমরফিজমের সাহায্যে, বিকাশকারীরা প্রথাগত সাব টাইপিং ব্যবহার না করেই বিভিন্ন অসংলগ্ন প্রকারের জন্য একই আচরণ প্রয়োগ করতে পারে (যা কোডকে যুক্ত করে)
  • নির্ভরতা ইনজেকশন সহজ করা
    • লিসকভ প্রতিস্থাপন নীতির ক্ষেত্রে একটি TC উদাহরণ পরিবর্তন করা যেতে পারে। 
    • যখন একটি উপাদান একটি TC উপর নির্ভরতা থাকে, একটি উপহাস করা TC সহজেই পরীক্ষার উদ্দেশ্যে ইনজেকশন করা যেতে পারে। 

পাল্টা ইঙ্গিত

প্রতিটি হাতুড়ি বিভিন্ন সমস্যার জন্য ডিজাইন করা হয়েছে।

টাইপ ক্লাসগুলি আচরণগত সমস্যার জন্য এবং ডেটা উত্তরাধিকারের জন্য ব্যবহার করা উচিত নয়। যে উদ্দেশ্যে রচনা ব্যবহার করুন.

সাধারণ সাবটাইপিং আরও সহজবোধ্য। আপনি যদি কোড বেসের মালিক হন এবং এক্সটেনসিবিলিটি লক্ষ্য না করেন, তাহলে টাইপ ক্লাস ওভারকিল হতে পারে।

উদাহরণস্বরূপ, স্কালা কোরে, একটি ` আছেNumeric` টাইপ ক্লাস:

scala

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`উদাহরণস্বরূপ)।

অন্যদিকে, স্কালা সংগ্রহের বাস্তবায়ন বেশিরভাগ ক্ষেত্রে টাইপ ক্লাসের পরিবর্তে সাবটাইপিং ব্যবহার করে। এই নকশাটি বিভিন্ন কারণে অর্থপূর্ণ:

  • সংগ্রহ API সম্পূর্ণ এবং স্থিতিশীল হতে অনুমিত হয়. এটি বাস্তবায়ন দ্বারা উত্তরাধিকারসূত্রে প্রাপ্ত বৈশিষ্ট্যের মাধ্যমে সাধারণ আচরণকে প্রকাশ করে। অত্যন্ত এক্সটেনসিবল হচ্ছে এখানে একটি নির্দিষ্ট লক্ষ্য নয়.
  • এটি ব্যবহার করা সহজ হতে হবে। TC শেষ ব্যবহারকারী প্রোগ্রামারে একটি মানসিক ওভারহেড যোগ করে।
  • পারফরম্যান্সের ক্ষেত্রে TCও ছোট ওভারহেড বহন করতে পারে। এটি একটি সংগ্রহ API এর জন্য গুরুত্বপূর্ণ হতে পারে।
  • যদিও, তৃতীয় পক্ষের লাইব্রেরি দ্বারা সংজ্ঞায়িত নতুন TC এর মাধ্যমে সংগ্রহ API এখনও এক্সটেনসিবল।

উপসংহার

আমরা দেখেছি যে TC একটি সাধারণ প্যাটার্ন যা একটি বড় সমস্যা সমাধান করে। স্কালা সমৃদ্ধ সিনট্যাক্সের জন্য ধন্যবাদ, TC প্যাটার্ন প্রয়োগ করা যেতে পারে এবং বিভিন্ন উপায়ে ব্যবহার করা যেতে পারে। টিসি প্যাটার্নটি কার্যকরী প্রোগ্রামিং দৃষ্টান্তের সাথে সঙ্গতিপূর্ণ এবং এটি একটি পরিষ্কার আর্কিটেকচারের জন্য একটি দুর্দান্ত হাতিয়ার। কোন সিলভার বুলেট নেই এবং এটি ফিট হলে TC প্যাটার্ন প্রয়োগ করতে হবে।

আশা করি আপনি এই নথিটি পড়ে জ্ঞান অর্জন করেছেন। 

কোড পাওয়া যায় https://github.com/jprudent/type-class-article. আপনার কোন ধরণের প্রশ্ন বা মন্তব্য থাকলে অনুগ্রহ করে আমার সাথে যোগাযোগ করুন। আপনি চাইলে রিপোজিটরিতে সমস্যা বা কোড মন্তব্য ব্যবহার করতে পারেন।


জেরোম বিচক্ষণ

সফটওয়্যার ইঞ্জিনিয়ার

সময় স্ট্যাম্প:

থেকে আরো খতিয়ান