این سند برای توسعه دهنده مبتدی Scala3 در نظر گرفته شده است که قبلاً به نثر Scala مسلط است، اما در مورد همه اینها متحیر است.implicits
` و صفات پارامتر شده در کد.
این سند چرایی، چگونه، کجا و چه زمانی را توضیح می دهد کلاس های نوع (TC).
پس از خواندن این سند، توسعه دهنده مبتدی Scala3 دانش کاملی برای استفاده و بررسی کد منبع آن به دست خواهد آورد. زیاد از کتابخانه های اسکالا و شروع به نوشتن کد اسکالا اصطلاحی کنید.
بیایید با دلیل…
مشکل بیان
در 1998، فیلیپ وادلر بیان کرد که "مشکل عبارت نام جدیدی برای یک مشکل قدیمی است". مشکل توسعه پذیری نرم افزار است. طبق نوشته آقای Wadler، راه حل مسئله بیان باید با قوانین زیر مطابقت داشته باشد:
- قانون 1: اجازه اجرای رفتارهای موجود (ویژگی Scala را در نظر بگیرید) که باید به آن اعمال شود نمایندگی های جدید (به یک کلاس موردی فکر کنید)
- قانون 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) یا سوئیچ های مبتنی بر تایپ می شود.
این قانون شماره 2 مشکل است. ما سعی کردیم آن را با تعریف خودمان از چندشکلی و ترفند «بسط» پیاده سازی کنیم. و این عجیب بود
یک قطعه گم شده به نام وجود دارد چند شکلی ad-hoc: توانایی ارسال ایمن اجرای یک رفتار بر اساس یک نوع، هر جا که رفتار و نوع آن تعریف شده باشد. را وارد کنید کلاس تایپ الگوی.
الگوی کلاس Type
دستور العمل الگوی کلاس Type (به اختصار TC) دارای 3 مرحله است.
- یک رفتار جدید را تعریف کنید
- رفتار را اجرا کنید
- از رفتار استفاده کن
در بخش بعدی، الگوی TC را به ساده ترین شکل پیاده سازی می کنم. این پرمخاطب، درهم و غیرعملی است. اما صبر کنید، این اخطارها گام به گام در سند رفع خواهند شد.
1. یک رفتار جدید تعریف کنید
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
`Lib1
` بار دیگر دست نخورده باقی مانده است.
رفتار جدید is TC توسط این ویژگی تحقق یافت. توابع تعریف شده در صفت راهی برای اعمال برخی از جنبه های آن رفتار است.
پارامتر `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
«چسب بین نمایش (الف واقعی) و رفتار آن است. داده ها و رفتار از هم جدا هستند، این همان چیزی است که برنامه نویسی کاربردی از آن حمایت می کند.
بحث
بیایید قواعد مسئله بیان را مرور کنیم:
- قانون 1: اجازه اجرای رفتارهای موجود اعمال شود کلاس های جدید
- قانون 2: اجازه اجرای رفتارهای جدید اعمال شود کلاس های موجود
- قانون 3: نباید به خطر بیفتد ایمنی نوع
- قانون 4: نباید نیاز به کامپایل مجدد داشته باشد کد موجود
قانون 1 را می توان با چند شکلی زیرگروهی حل کرد.
الگوی TC که به تازگی ارائه شده است (نگاه کنید به اسکرین شات قبلی) قانون 2 را حل می کند. از نوع امن است (قانون 3) و ما هرگز به آن دست نزدیم.Lib1
(قاعده 4).
اما استفاده از آن به چند دلیل غیرعملی است:
- در خطوط 33-34 باید رفتار را به طور صریح در امتداد نمونه آن منتقل کنیم. این یک سربار اضافی است. فقط باید بنویسیم
useLastBlock(Bitcoin())
`. - خط 31 نحو غیر معمول است. ما ترجیح می دهیم که مختصرتر و شی گراتر بنویسیم
instance.lastBlock()
` بیانیه
بیایید برخی از ویژگی های Scala را برای استفاده عملی از TC برجسته کنیم.
تجربه توسعه دهنده پیشرفته
Scala دارای مجموعه ای منحصر به فرد از ویژگی ها و قندهای نحوی است که TC را به یک تجربه واقعا لذت بخش برای توسعه دهندگان تبدیل می کند.
ضمنی
دامنه ضمنی یک محدوده ویژه است که در زمان کامپایل حل می شود که در آن فقط یک نمونه از یک نوع معین می تواند وجود داشته باشد.
یک برنامه یک نمونه را در محدوده ضمنی با ` قرار می دهدgiven
کلمه کلیدی یا یک برنامه می تواند یک نمونه را از محدوده ضمنی با کلمه کلیدی «بازیابی کندusing
`.
دامنه ضمنی در زمان کامپایل حل می شود، روشی برای تغییر آن به صورت پویا در زمان اجرا وجود دارد. اگر برنامه کامپایل شود، دامنه ضمنی حل می شود. در زمان اجرا، نمیتوان نمونههای ضمنی از دست رفته را در جایی که از آنها استفاده میشود، داشت. تنها سردرگمی ممکن ممکن است ناشی از استفاده از مثال ضمنی اشتباه باشد، اما این مسئله برای موجودی بین صندلی و صفحه کلید باقی مانده است.
با دامنه جهانی متفاوت است زیرا:
- به صورت متنی حل شده است. دو مکان از یک برنامه می توانند از یک نمونه از یک نوع معین در محدوده ضمنی استفاده کنند، اما این دو نمونه ممکن است متفاوت باشند.
- در پشت صحنه، کد تابع آرگومانهای ضمنی را ارسال میکند تا به استفاده ضمنی برسد. از فضای حافظه جهانی استفاده نمی کند.
برگردیم به کلاس تایپ! بیایید دقیقاً همین مثال را در نظر بگیریم.
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
کامپایلر به او می گوید.
Scala ضمنی کار عبور از نمونه های کلاس همراه با نمونه هایی از رفتار آنها را آسان می کند.
قندهای ضمنی
خط 22 و 25 کد قبلی را می توان بیشتر بهبود بخشید! بیایید پیاده سازی های TC را تکرار کنیم.
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 ما از TC در پسوند برای تعریف ` استفاده مجدد می کنیم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 استفاده میکنند و برنامهنویس را مجبور میکند تا آنها را در پایه کد خود پیادهسازی کند.
برای مثال Circe (یک کتابخانه سریالزدایی json) از TC ` استفاده میکندEncoder[T]
`و`Decoder[T]
برای برنامه نویسان برای پیاده سازی در پایگاه کد خود. پس از پیاده سازی، می توان از کل محدوده کتابخانه استفاده کرد.
این پیادهسازیهای TC بیشتر از مواقع هستند نقشه برداران داده گرا. آنها به هیچ منطق تجاری نیاز ندارند، برای نوشتن کسل کننده هستند، و باری برای همگام سازی با کلاس های موردی دارند.
در چنین شرایطی، آن کتابخانه ها چیزی را ارائه می دهند که به آن گفته می شود اتوماتیک اشتقاق یا نیمه خود کار استخراج. به عنوان مثال Circe را ببینید اتوماتیک و نیمه خود کار استخراج. با اشتقاق نیمه خودکار، برنامه نویس می تواند نمونه ای از یک کلاس نوع را با مقداری نحو جزئی اعلام کند، در حالی که اشتقاق خودکار نیازی به تغییر کد ندارد به جز واردات.
در زیر هود، در زمان کامپایل، ماکروهای عمومی دروننگری میشوند انواع به عنوان ساختار داده خالص و ایجاد یک TC[T] برای کاربران کتابخانه.
استخراج یک TC به طور کلی بسیار رایج است، بنابراین اسکالا جعبه ابزار کاملی را برای این منظور معرفی کرد. این روش همیشه توسط اسناد کتابخانه ای تبلیغ نمی شود، اگرچه این روش 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 تمرکز کنید. 2 نحو برای استخراج یک TC وجود دارد:
- خط 36 نمونه TC را می توان مستقیماً روی کلاس case با « تعریف کرد
derives
کلمه کلیدی در زیر هود، کامپایلر یک ` داده شده را تولید می کندNamed
"مثال در"Polkadot
` شیء همراه. - خط 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 می توانند از برنامه نویسی متا برای ارائه توابعی استفاده کنند که به طور کلی نمونه هایی از TC را با یک نوع خاص تولید می کنند. برنامه نویسان می توانند از API کتابخانه اختصاصی یا ابزار استخراج Scala برای ایجاد نمونه هایی برای کد خود استفاده کنند.
چه برای پیاده سازی یک TC به کد عمومی یا خاص نیاز داشته باشید، برای هر موقعیتی راه حلی وجود دارد.
خلاصه تمام مزایا
- مشکل بیان را حل می کند
- انواع جدید می توانند رفتار موجود را از طریق وراثت صفت سنتی پیاده سازی کنند
- رفتارهای جدید را می توان بر روی انواع موجود پیاده سازی کرد
- جداسازی نگرانی
- کد مخدوش نیست و به راحتی قابل حذف است. یک TC داده ها و رفتار را از هم جدا می کند، که یک شعار برنامه نویسی کاربردی است.
- امن است
- از نوع ایمن است زیرا به درون نگری متکی نیست. از تطبیق الگوهای بزرگ شامل انواع جلوگیری می کند. اگر با نوشتن چنین کدی مواجه شدید، ممکن است موردی را تشخیص دهید که الگوی TC کاملاً مناسب باشد.
- مکانیسم ضمنی کامپایل امن است! اگر یک نمونه در زمان کامپایل گم شده باشد، کد کامپایل نمی شود. بدون تعجب در زمان اجرا.
- چندشکلی موقتی را به ارمغان می آورد
- چند شکلی ad hoc معمولاً در برنامه نویسی شی گرا سنتی وجود ندارد.
- با چند شکلی ad-hoc، توسعه دهندگان می توانند رفتار یکسانی را برای انواع مختلف نامرتبط بدون استفاده از تایپ فرعی سنتی (که کد را جفت می کند) پیاده سازی کنند.
- تزریق وابستگی آسان شد
- یک نمونه TC را می توان با توجه به اصل جایگزینی Liskov تغییر داد.
- هنگامی که یک جزء به یک 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
استفاده از چنین کلاس نوع واقعاً منطقی است زیرا نه تنها امکان استفاده مجدد از الگوریتمهای جبری را در انواعی که در Scala تعبیه شدهاند (Int, BigInt,…) را میدهد، بلکه در انواع تعریفشده توسط کاربر (a`).ComplexNumber
به عنوان مثال).
از سوی دیگر، پیاده سازی مجموعه های اسکالا بیشتر از تایپ فرعی به جای کلاس نوع استفاده می کند. این طراحی به چند دلیل منطقی است:
- مجموعه API قرار است کامل و پایدار باشد. این رفتار مشترک را از طریق صفات به ارث رسیده توسط پیاده سازی ها نشان می دهد. در اینجا بسیار توسعه پذیر بودن هدف خاصی نیست.
- استفاده از آن باید ساده باشد. TC یک سربار ذهنی به برنامه نویس کاربر نهایی اضافه می کند.
- TC همچنین ممکن است سربار کمی را در عملکرد متحمل شود. این ممکن است برای یک مجموعه API حیاتی باشد.
- اگرچه، مجموعه API هنوز از طریق TC جدید تعریف شده توسط کتابخانه های شخص ثالث قابل گسترش است.
نتیجه
ما دیدیم که TC یک الگوی ساده است که یک مشکل بزرگ را حل می کند. به لطف نحو غنی Scala، الگوی TC را می توان به روش های مختلفی پیاده سازی و استفاده کرد. الگوی TC مطابق با الگوی برنامه نویسی عملکردی است و ابزاری شگفت انگیز برای یک معماری تمیز است. هیچ گلوله نقره ای وجود ندارد و الگوی TC باید در زمان مناسب اعمال شود.
امیدوارم با خواندن این سند دانش کسب کرده باشید.
کد در دسترس است https://github.com/jprudent/type-class-article. لطفا در صورت داشتن هرگونه سوال یا نکته با من تماس بگیرید. در صورت تمایل می توانید از مسائل یا نظرات کد در مخزن استفاده کنید.
مهندس نرمافزار
- محتوای مبتنی بر SEO و توزیع روابط عمومی. امروز تقویت شوید.
- PlatoData.Network Vertical Generative Ai. به خودت قدرت بده دسترسی به اینجا.
- PlatoAiStream. هوش وب 3 دانش تقویت شده دسترسی به اینجا.
- PlatoESG. کربن ، CleanTech، انرژی، محیط، خورشیدی، مدیریت پسماند دسترسی به اینجا.
- PlatoHealth. هوش بیوتکنولوژی و آزمایشات بالینی. دسترسی به اینجا.
- منبع: https://www.ledger.com/blog/type-classes-in-scala3-a-beginners-guide
- : دارد
- :است
- :نه
- :جایی که
- ][پ
- 1
- 10
- 11
- 12
- 14
- ٪۱۰۰
- 19
- 1998
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- ٪۱۰۰
- 36
- 7
- 8
- 9
- a
- توانایی
- درباره ما
- AC
- مطابق
- عمل
- واقعی
- می افزاید:
- طرفداران
- از نو
- هدف
- الگوریتم
- معرفی
- اجازه دادن
- اجازه می دهد تا
- در امتداد
- قبلا
- همچنین
- جایگزین
- هر چند
- همیشه
- an
- و
- دیگر
- هر
- API
- اعمال می شود
- درخواست
- مناسب
- معماری
- بایگانی
- هستند
- استدلال
- مقاله
- AS
- جنبه
- At
- کوشش
- نویسندگان
- اتوماتیک
- در دسترس
- اجتناب از
- به عقب
- پایه
- مستقر
- BE
- زیرا
- قبل از
- مبتدی
- رفتار
- بودن
- میان
- بزرگ
- بلاکچین
- خسته کننده
- هر دو
- جعبه
- به ارمغان می آورد
- پهن
- BTC
- بار
- کسب و کار
- اما
- by
- صدا
- نام
- دعوت کننده
- CAN
- مورد
- صندلی
- تغییر دادن
- تغییر
- کلاس
- کلاس ها
- تمیز
- رمز
- پایه کد
- پایه کد
- مجموعه
- مجموعه
- بیا
- می آید
- نظرات
- مشترک
- همراه و همدم
- کامل
- مطابق
- جزء
- ترکیب
- نگرانی
- مختصر
- نتیجه گیری می کند
- درگیری
- گیجی
- شامل
- هسته
- اصلاح
- میتوانست
- ایجاد
- ایجاد
- موجود
- بحرانی
- داده ها
- اعلام
- اختصاصی
- تعريف كردن
- مشخص
- تعریف می کند
- تعریف
- تعاریف
- وابستگی
- عمق
- استخراج
- طرح
- طراحی
- تشخیص
- توسعه دهنده
- توسعه دهندگان
- DID
- مختلف
- مستقیما
- ارسال
- شیرجه رفتن
- do
- سند
- میکند
- نمی کند
- آیا
- بطور پویا
- هر
- سهولت
- آسان تر
- به آسانی
- ساده
- ed
- جاسازی شده
- رویارویی
- پایان
- اعمال
- اجرای
- لذت بخش
- وارد
- خطاهای
- ETH
- اتر (ETH)
- حتی
- همه چیز
- کاملا
- مثال
- جز
- وجود داشته باشد
- موجود
- انتظار می رود
- انتظار می رود
- تجربه
- توضیح دهید
- توضیح می دهد
- به صراحت
- صریح
- بیان
- گسترش
- ضمیمهها
- اضافی
- نتواند
- امکانات
- احساس
- ثابت
- تمرکز
- تمرکز
- پیروی
- برای
- فرم
- از جانب
- تابع
- تابعی
- توابع
- بیشتر
- افزایش
- به دست آورد
- تولید می کنند
- تولید می کند
- GitHub
- داده
- دادن
- جهانی
- گستره جهانی
- هدف
- راهنمایی
- چکش
- دست
- اتفاق می افتد
- آیا
- اینجا کلیک نمایید
- نماد
- خیلی
- او را
- نگه داشتن
- کاپوت
- چگونه
- HTML
- HTTP
- HTTPS
- i
- if
- وهم
- تصور کنید
- انجام
- پیاده سازی
- پیاده سازی ها
- اجرا
- پیاده سازی می کند
- واردات
- واردات
- بهبود یافته
- in
- از جمله
- وراثت
- نمونه
- نمونه ها
- در عوض
- مورد نظر
- به
- معرفی
- شامل
- موضوع
- مسائل
- IT
- ITS
- به خطر اندازد
- json
- تنها
- نگاه داشتن
- دانستن
- دانش
- زبان
- نام
- منجر می شود
- ترک
- دفتر کل
- ترک کرد
- کمتر
- اهرم ها
- کتابخانه ها
- کتابخانه
- پسندیدن
- لاین
- خطوط
- لینک
- مکان
- منطق
- خیلی
- ماکرو
- ساخته
- آسان ساخته شده است
- شعبده بازي
- حفظ
- ساخت
- باعث می شود
- روش
- بسیاری
- مطابق
- ممکن است..
- me
- مکانیزم
- حافظه
- روانی
- تولید گزارشات تاریخی
- متا
- روش
- قدرت
- ذهن
- خردسال
- آینه
- گم
- بیش
- اکثر
- اغلب
- شعار
- باید
- نام
- تحت عنوان
- نامگذاری
- طبیعی
- نیاز
- ضروری
- هرگز
- جدید
- نه
- توجه داشته باشید
- هدف
- of
- ارائه
- پیشنهادات
- غالبا
- قدیمی
- on
- یک بار
- ONE
- فقط
- or
- دیگر
- ما
- خارج
- نمای کلی
- روی
- خود
- نمونه
- پارامتر
- پارامترهای
- بخش
- ویژه
- حزب
- عبور
- گذشت
- عبور می کند
- عبور
- الگو
- کاملا
- کارایی
- قطعه
- افلاطون
- هوش داده افلاطون
- PlatoData
- لطفا
- ممکن
- عملی
- ترجیح می دهند
- ارائه شده
- قبلی
- قبلا
- اصل
- مشکل
- مشکلات
- محصول
- برنامه
- برنامهنویس
- برنامه نویسان
- برنامه نويسي
- مناسب
- ارائه
- هدف
- اهداف
- قرار دادن
- قرار می دهد
- سوالات
- محدوده
- نسبتا
- رسیدن به
- رسیده
- مطالعه
- واقعا
- دلیل
- خلاصه
- دستور العمل
- تکیه
- ماندن
- به یاد داشته باشید
- از بین بردن
- جایگزین
- مخزن
- نمایندگی
- نشان دهنده
- مصمم
- احترام
- استفاده مجدد
- غنی
- قانون
- قوانین
- s
- امن
- با خیال راحت
- ایمنی
- دلیل
- سام
- همان
- اسکالا
- صحنه
- حوزه
- بخش
- بخش
- دیدن
- مشاهده گردید
- حس
- تنظیم
- چند
- کوتاه
- باید
- نقره
- ساده
- ساده کردن
- ساده
- تنها
- وضعیت
- کوچک
- So
- نرم افزار
- جامد
- راه حل
- حل شد
- حل می کند
- برخی از
- چیزی
- منبع
- کد منبع
- فضا
- صحبت کردن
- ویژه
- خاص
- به طور خاص
- پایدار
- شروع
- بیانیه
- گام
- مراحل
- هنوز
- ساده
- ساده کردن
- رشته
- ساختار
- چنین
- قند
- نشان می دهد
- کت و شلوار
- مفروض
- مطمئن
- تعجب
- گزینه
- همگام سازی
- نحو
- سیستم
- T
- گرفتن
- کار
- می گوید
- تست
- نسبت به
- با تشکر
- که
- La
- منبع
- شان
- آنها
- آنجا.
- آنها
- چیز
- اشیاء
- فکر می کنم
- سوم
- این
- کسانی که
- اگر چه؟
- از طریق
- زمان
- به
- ابزار
- جعبه ابزار
- ابزار
- لمس کرد
- سنتی
- سعی
- صادقانه
- امتحان
- دو
- نوع
- انواع
- غیر معمول
- زیر
- منحصر به فرد
- بی نام
- تا
- دست نخورده
- استفاده نشده
- بر
- استفاده
- استفاده کنید
- استفاده
- کاربر
- کاربران
- استفاده
- با استفاده از
- معمول
- معمولا
- ارزش
- مختلف
- متبحر
- بسیار
- می خواهم
- بود
- مسیر..
- راه
- we
- چی
- چه شده است
- چه زمانی
- در حالیکه
- که
- WHO
- تمام
- چرا
- به طور گسترده ای
- اراده
- عاقلانه
- با
- بدون
- خواهد بود
- نوشتن
- نوشته
- اشتباه
- هنوز
- شما
- شما
- خودت
- زفیرنت