Dokumen ini ditujukan untuk pengembang Scala3 pemula yang sudah berpengalaman dalam prosa Scala, namun bingung dengan semua `implicits
` dan sifat-sifat yang diparameterisasi dalam kode.
Dokumen ini menjelaskan mengapa, bagaimana, dimana dan kapan Jenis Kelas (TC).
Setelah membaca dokumen ini, pengembang Scala3 pemula akan memperoleh pengetahuan yang kuat untuk menggunakan dan mendalami kode sumbernya banyak perpustakaan Scala dan mulai menulis kode Scala idiomatik.
Mari kita mulai dengan alasannya…
Masalah ekspresi
Dalam 1998, Philip Wadler menyatakan bahwa “masalah ekspresi adalah nama baru untuk masalah lama”. Ini adalah masalah ekstensibilitas perangkat lunak. Menurut tulisan Pak Wadler, penyelesaian masalah ekspresi harus mematuhi aturan berikut:
- Aturan 1: Izinkan penerapan perilaku yang ada (pikirkan sifat Scala) untuk diterapkan representasi baru (pikirkan kelas kasus)
- Aturan 2: Izinkan penerapan perilaku baru untuk diterapkan representasi yang ada
- Aturan 3: Tidak boleh membahayakan ketik keamanan
- Aturan 4: Tidak perlu dikompilasi ulang kode yang ada
Memecahkan masalah ini akan menjadi benang merah artikel ini.
Aturan 1: implementasi perilaku yang ada pada representasi baru
Bahasa berorientasi objek apa pun memiliki solusi bawaan untuk aturan 1 dengan polimorfisme subtipe. Anda dapat dengan aman mengimplementasikan `trait
` didefinisikan dalam ketergantungan pada `class
` dalam kode Anda sendiri, tanpa mengkompilasi ulang ketergantungan. Mari kita lihat aksinya:
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()
Dalam contoh fiktif ini, perpustakaan `Lib1
` (baris 5) mendefinisikan suatu sifat `Blockchain
` (baris 6) dengan 2 implementasinya (baris 9 & 12). `Lib1
` akan tetap sama di SEMUA dokumen ini (penerapan aturan 4).
`Lib2
` (baris 15) mengimplementasikan perilaku yang ada `Blockchain
`di kelas baru`Polkadot
` (aturan 1) dengan cara tipe aman (aturan 3), tanpa kompilasi ulang `Lib1
` (aturan 4).
Aturan 2: penerapan perilaku baru untuk diterapkan pada representasi yang sudah ada
Mari kita bayangkan di `Lib2
`kami ingin perilaku baru`lastBlock
` untuk diterapkan secara khusus untuk masing-masing `Blockchain
`.
Hal pertama yang terlintas dalam pikiran adalah membuat saklar besar berdasarkan jenis parameter.
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()))
Solusi ini adalah implementasi ulang yang lemah dari polimorfisme berbasis tipe yang sudah ada dalam bahasa!
`Lib1
` tidak tersentuh (ingat, aturan 4 diterapkan di seluruh dokumen ini).
Solusi diimplementasikan di `Lib2
` adalah oke sampai blockchain lain diperkenalkan di `Lib3
`. Itu melanggar aturan keamanan tipe (aturan 3) karena kode ini gagal saat runtime di baris 37. Dan memodifikasi `Lib2
` akan melanggar aturan 4.
Solusi lain adalah menggunakan `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
` tidak tersentuh (penerapan aturan 4 di seluruh dokumen).
`Lib2
` mendefinisikan perilaku untuk tipenya (baris 21) dan `ekstensi` untuk tipe yang ada (baris 23 & 25).
Baris 28-30, behavior baru dapat digunakan di setiap kelas.
Namun tidak ada cara untuk menyebut perilaku baru ini secara polimorfik (baris 32). Setiap upaya untuk melakukannya akan menyebabkan kesalahan kompilasi (baris 33) atau switch berbasis tipe.
Peraturan n°2 ini rumit. Kami mencoba menerapkannya dengan definisi polimorfisme dan trik `ekstensi` kami sendiri. Dan itu aneh.
Ada bagian yang hilang disebut polimorfisme ad-hoc: kemampuan untuk mengirimkan implementasi perilaku dengan aman menurut suatu tipe, di mana pun perilaku dan tipe tersebut ditentukan. Masukkan Tipe Kelas pola.
Pola Kelas Tipe
Resep pola Type Class (singkatnya TC) memiliki 3 langkah.
- Tentukan perilaku baru
- Terapkan perilaku tersebut
- Gunakan perilaku tersebut
Di bagian berikut, saya menerapkan pola TC dengan cara yang paling mudah. Ini bertele-tele, kikuk dan tidak praktis. Namun tunggu dulu, peringatan tersebut akan diperbaiki langkah demi langkah lebih lanjut dalam dokumen.
1. Definisikan perilaku baru
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
`Lib1
` sekali lagi, tidak tersentuh.
Perilaku baru is TC diwujudkan oleh sifat tersebut. Fungsi yang didefinisikan dalam suatu sifat adalah cara untuk menerapkan beberapa aspek perilaku tersebut.
Parameter `A
` mewakili tipe yang ingin kita terapkan perilakunya, yang merupakan subtipe dari `Blockchain
` dalam kasus kami.
Beberapa komentar:
- Jika diperlukan, tipe parameter `
A
` selanjutnya dapat dibatasi oleh sistem tipe Scala. Misalnya, kita dapat menerapkan `A
` menjadi `Blockchain
`. - Selain itu, TC dapat memiliki lebih banyak fungsi yang dideklarasikan di dalamnya.
- Terakhir, setiap fungsi mungkin memiliki lebih banyak parameter arbitrer.
Tapi mari kita buat semuanya tetap sederhana demi keterbacaan.
2. Menerapkan perilaku tersebut
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")
Untuk setiap jenis yang baru `LastBlock
` perilaku yang diharapkan, ada contoh spesifik dari perilaku tersebut.
`Ethereum
` implementasi baris 22 dihitung dari `eth
` instance diteruskan sebagai parameter.
Implementasi `LastBlock
`untuk`Bitcoin
` baris 25 diimplementasikan dengan IO yang tidak dikelola dan tidak menggunakan parameternya.
Jadi, `Lib2
` mengimplementasikan perilaku baru `LastBlock
`untuk`Lib1
` kelas.
3. Gunakan perilaku tersebut
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))
Baris 30`useLastBlock
` menggunakan contoh `A
` dan `LastBlock
` perilaku yang ditentukan untuk contoh itu.
Baris 33`useLastBlock
` dipanggil dengan instance `Ethereum
` dan implementasi dari `LastBlock
` didefinisikan dalam `Lib2
`. Perhatikan bahwa penerapan alternatif apa pun dari ` dapat diteruskanLastBlock[A]
` (pikirkan injeksi ketergantungan).
`useLastBlock
` adalah perekat antara representasi (A sebenarnya) dan perilakunya. Data dan perilaku dipisahkan, itulah yang dianjurkan oleh pemrograman fungsional.
Diskusi
Mari kita rekap aturan soal ekspresi:
- Aturan 1: Izinkan penerapan perilaku yang ada untuk diterapkan kelas baru
- Aturan 2: Izinkan penerapan perilaku baru untuk diterapkan kelas yang ada
- Aturan 3: Tidak boleh membahayakan ketik keamanan
- Aturan 4: Tidak perlu dikompilasi ulang kode yang ada
Aturan 1 dapat diselesaikan secara langsung dengan polimorfisme subtipe.
Pola TC yang baru saja disajikan (lihat tangkapan layar sebelumnya) menyelesaikan aturan 2. Tipenya aman (aturan 3) dan kami tidak pernah menyentuh `Lib1
` (aturan 4).
Namun ini tidak praktis untuk digunakan karena beberapa alasan:
- Baris 33-34 kita harus meneruskan perilaku secara eksplisit di sepanjang instance-nya. Ini adalah biaya tambahan. Kita sebaiknya menulis saja `
useLastBlock(Bitcoin())
`. - Baris 31 sintaksnya tidak umum. Kami lebih memilih untuk menulis secara ringkas dan lebih berorientasi objek ``
instance.lastBlock()
` pernyataan.
Mari kita soroti beberapa fitur Scala untuk penggunaan TC praktis.
Pengalaman pengembang yang ditingkatkan
Scala memiliki serangkaian fitur unik dan gula sintaksis yang menjadikan TC pengalaman yang benar-benar menyenangkan bagi pengembang.
Tersirat
Cakupan implisit adalah cakupan khusus yang diselesaikan pada waktu kompilasi di mana hanya satu instance dari tipe tertentu yang dapat ada.
Sebuah program menempatkan sebuah instance dalam lingkup implisit dengan `given
` kata kunci. Alternatifnya, suatu program dapat mengambil sebuah instance dari cakupan implisit dengan kata kunci `using
`.
Cakupan implisit diselesaikan pada waktu kompilasi, ada cara untuk mengubahnya secara dinamis saat runtime. Jika program dikompilasi, cakupan implisitnya teratasi. Pada saat runtime, tidak mungkin ada instance implisit yang hilang saat instance tersebut digunakan. Satu-satunya kebingungan yang mungkin terjadi mungkin berasal dari penggunaan contoh implisit yang salah, tetapi masalah ini diserahkan pada makhluk antara kursi dan keyboard.
Berbeda dengan cakupan global karena:
- Ini diselesaikan secara kontekstual. Dua lokasi program dapat menggunakan sebuah instance dengan tipe tertentu yang sama dalam cakupan implisit, namun kedua instance tersebut mungkin berbeda.
- Di belakang layar, kode meneruskan fungsi argumen implisit ke fungsi hingga penggunaan implisit tercapai. Itu tidak menggunakan ruang memori global.
Kembali ke kelas tipe! Mari kita ambil contoh yang sama persis.
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
` adalah kode yang sama yang tidak dimodifikasi yang kami definisikan sebelumnya.
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()))
Baris 19 perilaku baru `LastBlock
` didefinisikan, persis seperti yang kita lakukan sebelumnya.
Baris 22 dan baris 25, `val
` digantikan oleh `given
`. Kedua implementasi `LastBlock
` ditempatkan dalam lingkup implisit.
Baris 31`useLastBlock
` mendeklarasikan perilaku `LastBlock
` sebagai parameter implisit. Kompiler menyelesaikan contoh yang sesuai dari `LastBlock
` dari cakupan implisit yang dikontekstualisasikan dari lokasi penelepon (baris 33 dan 34). Baris 28 mengimpor semuanya dari `Lib2
`, termasuk cakupan implisit. Jadi, kompiler meneruskan instance yang ditentukan baris 22 dan 25 sebagai parameter terakhir `useLastBlock
`.
Sebagai pengguna perpustakaan, menggunakan kelas tipe lebih mudah dari sebelumnya. Baris 34 dan 35 pengembang hanya perlu memastikan bahwa sebuah instance dari perilaku dimasukkan ke dalam cakupan implisit (dan ini bisa berupa `hanyaimport
`). Jika implisit bukan `given
`di mana kodenya`using
` itu, kompiler memberitahunya.
Implisit Scala memudahkan tugas meneruskan instance kelas beserta contoh perilakunya.
Gula implisit
Baris 22 dan 25 dari kode sebelumnya dapat ditingkatkan lebih lanjut! Mari kita ulangi penerapan 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")
Baris 22 dan 25, jika nama instance tidak digunakan, dapat dihilangkan.
given LastBlock[Ethereum] with
def lastBlock(eth: Ethereum) = eth.lastBlock
given LastBlock[Bitcoin] with
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
Baris 22 dan 25, pengulangan tipenya dapat diganti dengan `with
` kata kunci.
given LastBlock[Ethereum] = _.lastBlock
given LastBlock[Bitcoin] = _ => http("http://bitcoin/last")
Karena kami menggunakan sifat yang mengalami degenerasi dengan satu fungsi di dalamnya, IDE mungkin menyarankan untuk menyederhanakan kode dengan ekspresi SAM. Meskipun benar, menurut saya ini bukanlah penggunaan SAM yang tepat, kecuali Anda sedang bermain golf dengan santai.
Scala menawarkan gula sintaksis untuk menyederhanakan sintaksis, menghilangkan penamaan, deklarasi, dan redundansi tipe yang tidak perlu.
Perpanjangan
Digunakan dengan bijak, `extension
Mekanisme ` dapat menyederhanakan sintaks untuk menggunakan kelas tipe.
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)
Baris 28-29 merupakan metode ekstensi umum `lastBlock
` didefinisikan untuk semua `A
` dengan `LastBlock
` Parameter TC dalam cakupan implisit.
Baris 33-34 ekstensi memanfaatkan sintaks berorientasi objek untuk menggunakan 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)
Baris 28, parameter TC juga dapat ditentukan untuk keseluruhan ekstensi untuk menghindari pengulangan. Baris 30 kami menggunakan kembali TC di ekstensi untuk mendefinisikan `penultimateBlock
` (walaupun bisa diterapkan pada `LastBlock
` sifat secara langsung)
Keajaiban terjadi ketika TC digunakan. Ekspresinya terasa lebih alami, memberikan ilusi perilaku `lastBlock
` digabungkan dengan instance.
Tipe generik dengan 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))
Baris 34 fungsinya menggunakan TC implisit. Perhatikan bahwa TC tidak perlu disebutkan namanya jika nama tersebut tidak diperlukan.
Pola TC digunakan secara luas sehingga terdapat sintaks tipe generik untuk mengekspresikan “tipe dengan perilaku implisit”. Sintaks baris 36 merupakan alternatif yang lebih ringkas dari yang sebelumnya (baris 34). Ini menghindari deklarasi secara spesifik parameter TC implisit yang tidak disebutkan namanya.
Ini mengakhiri bagian pengalaman pengembang. Kita telah melihat bagaimana ekstensi, implisit, dan beberapa gula sintaksis dapat memberikan sintaksis yang tidak terlalu berantakan ketika TC digunakan dan didefinisikan.
Derivasi otomatis
Banyak perpustakaan Scala menggunakan TC, membiarkan pemrogram mengimplementasikannya dalam basis kode mereka.
Misalnya Circe (perpustakaan de-serialisasi json) menggunakan TC `Encoder[T]
` dan `Decoder[T]
` untuk diimplementasikan oleh pemrogram dalam basis kode mereka. Setelah diimplementasikan, seluruh ruang lingkup perpustakaan dapat digunakan.
Penerapan TC tersebut lebih sering terjadi pembuat peta berorientasi data. Mereka tidak memerlukan logika bisnis apa pun, membosankan untuk ditulis, dan menjadi beban untuk tetap sinkron dengan kelas kasus.
Dalam situasi seperti ini, perpustakaan tersebut menawarkan apa yang disebut otomatis turunan atau setengah otomatis penurunan. Lihat misalnya Circe otomatis dan setengah otomatis penurunan. Dengan derivasi semi-otomatis, pemrogram dapat mendeklarasikan turunan kelas tipe dengan beberapa sintaks minor, sedangkan derivasi otomatis tidak memerlukan modifikasi kode apa pun kecuali untuk impor.
Di balik terpal, pada waktu kompilasi, makro generik melakukan introspeksi jenis sebagai struktur data murni dan menghasilkan TC[T] untuk pengguna perpustakaan.
Mendapatkan TC secara umum adalah hal yang sangat umum, jadi Scala memperkenalkan kotak alat lengkap untuk tujuan tersebut. Metode ini tidak selalu diiklankan oleh dokumentasi perpustakaan meskipun ini adalah cara Scala 3 menggunakan derivasi.
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)
Baris 18 adalah TC baru `Named
` diperkenalkan. TC ini sebenarnya tidak ada hubungannya dengan bisnis blockchain. Tujuannya adalah memberi nama blockchain berdasarkan nama kelas kasusnya.
Fokus pertama pada definisi baris 36-38. Ada 2 sintaksis untuk mendapatkan TC:
- Baris 36 instance TC dapat didefinisikan secara langsung pada kelas kasus dengan `
derives
` kata kunci. Di balik terpal, kompiler menghasilkan `Named
` contoh di `Polkadot
` objek pendamping. - Baris 37 dan 38, instance kelas tipe diberikan pada kelas yang sudah ada sebelumnya dengan `
TC.derived
`
Baris 31 ekstensi umum didefinisikan (lihat bagian sebelumnya) dan `blockchainName
` digunakan secara alami.
`derives
` kata kunci mengharapkan metode dengan bentuk`inline def derived[T](using Mirror.Of[T]): TC[T] = ???
` yang didefinisikan pada baris 24. Saya tidak akan menjelaskan secara mendalam apa fungsi kode tersebut. Secara garis besar:
- `
inline def
` mendefinisikan makro - `
Mirror
` adalah bagian dari kotak alat untuk melakukan introspeksi tipe. Ada berbagai jenis mirror, dan baris 26 kodenya berfokus pada `Product
` mirror (kelas kasus adalah produk). Baris 27, jika pemrogram mencoba menurunkan sesuatu yang bukan `Product
`, kode tidak dapat dikompilasi. - `
Mirror
` berisi tipe lain. Salah satunya, `MirrorLabel
`, adalah string yang berisi nama tipe. Nilai ini digunakan dalam implementasi, baris 29, dari `Named
`TC.
Penulis TC dapat menggunakan pemrograman meta untuk menyediakan fungsi yang secara umum menghasilkan instance TC berdasarkan tipenya. Pemrogram dapat menggunakan API perpustakaan khusus atau alat turunan Scala untuk membuat instance untuk kode mereka.
Baik Anda memerlukan kode umum atau spesifik untuk mengimplementasikan TC, selalu ada solusi untuk setiap situasi.
Ringkasan semua manfaat
- Ini memecahkan masalah ekspresi
- Tipe baru dapat menerapkan perilaku yang ada melalui pewarisan sifat tradisional
- Perilaku baru dapat diimplementasikan pada tipe yang sudah ada
- Pemisahan kekhawatiran
- Kode tidak rusak dan mudah dihapus. TC memisahkan data dan perilaku, yang merupakan moto pemrograman fungsional.
- Itu aman
- Tipe ini aman karena tidak mengandalkan introspeksi. Ini menghindari pencocokan pola besar yang melibatkan tipe. jika Anda menemukan diri Anda menulis kode seperti itu, Anda mungkin mendeteksi kasus di mana pola TC akan sangat cocok.
- Mekanisme implisitnya aman untuk dikompilasi! Jika sebuah instance hilang pada waktu kompilasi, kode tidak akan dapat dikompilasi. Tidak mengherankan saat runtime.
- Ini membawa polimorfisme ad-hoc
- Polimorfisme ad hoc biasanya tidak ada dalam pemrograman berorientasi objek tradisional.
- Dengan polimorfisme ad-hoc, pengembang dapat menerapkan perilaku yang sama untuk berbagai tipe yang tidak terkait tanpa menggunakan sub pengetikan tradisional (yang memasangkan kode)
- Injeksi ketergantungan menjadi mudah
- Contoh TC dapat diubah sehubungan dengan prinsip substitusi Liskov.
- Ketika komponen memiliki ketergantungan pada TC, TC tiruan dapat dengan mudah dimasukkan untuk tujuan pengujian.
Kontra indikasi
Setiap palu dirancang untuk berbagai masalah.
Kelas Tipe ditujukan untuk masalah perilaku dan tidak boleh digunakan untuk pewarisan data. Gunakan komposisi untuk tujuan itu.
Subtipe biasa lebih mudah. Jika Anda memiliki basis kode dan tidak menginginkan ekstensibilitas, kelas tipe mungkin berlebihan.
Misalnya, di inti Scala, ada `Numeric
` ketik kelas:
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
Sangat masuk akal untuk menggunakan kelas tipe seperti itu karena tidak hanya memungkinkan penggunaan kembali algoritma aljabar pada tipe yang tertanam dalam Scala (Int, BigInt,…), tetapi juga pada tipe yang ditentukan pengguna (a `ComplexNumber
` misalnya).
Di sisi lain, implementasi koleksi Scala sebagian besar menggunakan subtipe daripada kelas tipe. Desain ini masuk akal karena beberapa alasan:
- API pengumpulan seharusnya lengkap dan stabil. Ini memperlihatkan perilaku umum melalui sifat-sifat yang diwarisi oleh implementasi. Menjadi sangat dapat diperluas bukanlah tujuan khusus di sini.
- Itu harus mudah digunakan. TC menambah beban mental pada pemrogram pengguna akhir.
- TC mungkin juga menimbulkan sedikit overhead dalam kinerja. Ini mungkin penting untuk API koleksi.
- Meskipun demikian, API koleksi masih dapat diperluas melalui TC baru yang ditentukan oleh perpustakaan pihak ketiga.
Kesimpulan
Kita telah melihat bahwa TC adalah pola sederhana yang memecahkan masalah besar. Berkat sintaksis Scala yang kaya, pola TC dapat diimplementasikan dan digunakan dalam banyak cara. Pola TC sejalan dengan paradigma pemrograman fungsional dan merupakan alat luar biasa untuk arsitektur bersih. Tidak ada solusi yang tepat dan pola TC harus diterapkan jika cocok.
Semoga Anda memperoleh pengetahuan dengan membaca dokumen ini.
Kode tersedia di https://github.com/jprudent/type-class-article. Silakan hubungi saya jika Anda memiliki pertanyaan atau komentar. Anda dapat menggunakan isu atau komentar kode di repositori jika Anda mau.
Software Engineer
- Konten Bertenaga SEO & Distribusi PR. Dapatkan Amplifikasi Hari Ini.
- PlatoData.Jaringan Vertikal Generatif Ai. Berdayakan Diri Anda. Akses Di Sini.
- PlatoAiStream. Intelijen Web3. Pengetahuan Diperkuat. Akses Di Sini.
- PlatoESG. Karbon, teknologi bersih, energi, Lingkungan Hidup, Tenaga surya, Penanganan limbah. Akses Di Sini.
- PlatoHealth. Kecerdasan Uji Coba Biotek dan Klinis. Akses Di Sini.
- Sumber: https://www.ledger.com/blog/type-classes-in-scala3-a-beginners-guide
- :memiliki
- :adalah
- :bukan
- :Di mana
- ][P
- 1
- 10
- 11
- 12
- 14
- 15%
- 19
- 1998
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 35%
- 36
- 7
- 8
- 9
- a
- kemampuan
- Tentang Kami
- AC
- Menurut
- Tindakan
- sebenarnya
- Menambahkan
- pendukung
- lagi
- tujuan
- algoritma
- Semua
- mengizinkan
- memungkinkan
- sepanjang
- sudah
- juga
- alternatif
- Meskipun
- selalu
- an
- dan
- Lain
- Apa pun
- api
- terapan
- Mendaftar
- sesuai
- arsitektur
- arsip
- ADALAH
- argumen
- artikel
- AS
- aspek
- At
- usaha
- penulis
- secara otomatis
- tersedia
- menghindari
- kembali
- mendasarkan
- berdasarkan
- BE
- karena
- sebelum
- Pemula
- laku
- makhluk
- antara
- Besar
- blockchain
- Membosankan
- kedua
- Kotak
- Membawa
- luas
- BTC
- beban
- bisnis
- tapi
- by
- panggilan
- bernama
- pemanggil
- CAN
- kasus
- Kursi
- perubahan
- berubah
- kelas
- kelas-kelas
- membersihkan
- kode
- basis kode
- Basis kode
- koleksi
- koleksi
- bagaimana
- datang
- komentar
- Umum
- teman
- lengkap
- memenuhi
- komponen
- komposisi
- Perhatian
- ringkas
- menyimpulkan
- disatukan
- kebingungan
- mengandung
- Core
- benar
- bisa
- membuat
- membuat
- makhluk
- kritis
- data
- menyatakan
- dedicated
- menetapkan
- didefinisikan
- Mendefinisikan
- definisi
- definisi
- Ketergantungan
- kedalaman
- memperoleh
- Mendesain
- dirancang
- menemukan
- Pengembang
- pengembang
- MELAKUKAN
- berbeda
- langsung
- Pengiriman
- menyelam
- do
- dokumen
- tidak
- Tidak
- Dont
- dinamis
- setiap
- memudahkan
- mudah
- mudah
- Mudah
- ed
- tertanam
- pertemuan
- akhir
- melaksanakan
- pelaksanaan
- nikmat
- Enter
- kesalahan
- ETH
- Eter (ETH)
- Bahkan
- segala sesuatu
- persis
- contoh
- Kecuali
- ada
- ada
- diharapkan
- mengharapkan
- pengalaman
- Menjelaskan
- Menjelaskan
- secara eksplisit
- ekspres
- ekspresi
- perpanjangan
- ekstensi
- tambahan
- gagal
- Fitur
- terasa
- tetap
- Fokus
- berfokus
- berikut
- Untuk
- bentuk
- dari
- fungsi
- fungsionil
- fungsi
- lebih lanjut
- Mendapatkan
- diperoleh
- menghasilkan
- menghasilkan
- GitHub
- diberikan
- Pemberian
- Aksi
- lingkup global
- tujuan
- membimbing
- palu
- tangan
- Terjadi
- Memiliki
- di sini
- Menyoroti
- sangat
- dia
- memegang
- kap
- Seterpercayaapakah Olymp Trade? Kesimpulan
- HTML
- http
- HTTPS
- i
- if
- Ilusi
- membayangkan
- melaksanakan
- implementasi
- implementasi
- diimplementasikan
- mengimplementasikan
- mengimpor
- impor
- ditingkatkan
- in
- Termasuk
- warisan
- contoh
- contoh
- sebagai gantinya
- dimaksudkan
- ke
- diperkenalkan
- melibatkan
- isu
- masalah
- IT
- NYA
- Membahayakan
- json
- hanya
- Menjaga
- Tahu
- pengetahuan
- bahasa
- Terakhir
- Memimpin
- meninggalkan
- Buku besar
- meninggalkan
- kurang
- memanfaatkan
- perpustakaan
- Perpustakaan
- 'like'
- baris
- baris
- lokasi
- logika
- Lot
- Macro
- terbuat
- Made Easy
- sihir
- memelihara
- membuat
- MEMBUAT
- cara
- banyak
- sesuai
- Mungkin..
- me
- mekanisme
- Memori
- mental yang
- mer
- meta
- metode
- mungkin
- keberatan
- minor
- cermin
- hilang
- lebih
- paling
- kebanyakan
- Motto
- harus
- nama
- Bernama
- penamaan
- Alam
- Perlu
- dibutuhkan
- tak pernah
- New
- tidak
- mencatat
- obyek
- of
- menawarkan
- Penawaran
- sering
- Tua
- on
- sekali
- ONE
- hanya
- or
- Lainnya
- kami
- di luar
- menguraikan
- lebih
- sendiri
- pola pikir
- parameter
- parameter
- bagian
- tertentu
- pihak
- lulus
- Lulus
- melewati
- Lewat
- pola
- benar-benar
- prestasi
- bagian
- plato
- Kecerdasan Data Plato
- Data Plato
- silahkan
- mungkin
- Praktis
- lebih suka
- disajikan
- sebelumnya
- sebelumnya
- prinsip
- Masalah
- masalah
- Produk
- program
- Programmer
- Programmer
- Pemrograman
- tepat
- memberikan
- tujuan
- tujuan
- menempatkan
- Menempatkan
- Pertanyaan
- jarak
- agak
- mencapai
- tercapai
- Bacaan
- benar-benar
- alasan
- rekap
- resep
- mengandalkan
- tinggal
- ingat
- menghapus
- diganti
- gudang
- perwakilan
- merupakan
- diselesaikan
- menghormati
- menggunakan kembali
- Kaya
- Aturan
- aturan
- s
- aman
- aman
- Safety/keselamatan
- sake
- Universitas
- sama
- Scala
- adegan
- cakupan
- Bagian
- bagian
- melihat
- terlihat
- rasa
- set
- beberapa
- Pendek
- harus
- Silver
- Sederhana
- menyederhanakan
- menyederhanakan
- tunggal
- situasi
- kecil
- So
- Perangkat lunak
- padat
- larutan
- dipecahkan
- Memecahkan
- beberapa
- sesuatu
- sumber
- kode sumber
- Space
- berbicara
- khusus
- tertentu
- Secara khusus
- stabil
- awal
- Pernyataan
- Langkah
- Tangga
- Masih
- mudah
- mempersingkat
- Tali
- struktur
- seperti itu
- gula
- menyarankan
- setelan
- Seharusnya
- yakin
- mengherankan
- Beralih
- sinkronisasi.
- sintaksis
- sistem
- T
- Mengambil
- tugas
- mengatakan
- pengujian
- dari
- Terima kasih
- bahwa
- Grafik
- Sumber
- mereka
- Mereka
- Sana.
- mereka
- hal
- hal
- berpikir
- Ketiga
- ini
- itu
- meskipun?
- Melalui
- waktu
- untuk
- alat
- Toolbox
- alat
- tersentuh
- tradisional
- mencoba
- benar-benar
- mencoba
- dua
- mengetik
- jenis
- Luar biasa
- bawah
- unik
- TANPA NAMA
- sampai
- utuh
- terpakai
- atas
- penggunaan
- menggunakan
- bekas
- Pengguna
- Pengguna
- kegunaan
- menggunakan
- biasa
- biasanya
- nilai
- berbagai
- berpengalaman
- sangat
- ingin
- adalah
- Cara..
- cara
- we
- Apa
- Apa itu
- ketika
- sedangkan
- yang
- SIAPA
- seluruh
- mengapa
- sangat
- akan
- dengan bijak
- dengan
- tanpa
- akan
- menulis
- penulisan
- Salah
- namun
- kamu
- Anda
- diri
- zephyrnet.dll