Zephyrnet Logosu

Scala3'te Sınıfları Yazın: Yeni Başlayanlar İçin Kılavuz | Defter

Tarih:

Bu belge, Scala düzyazısında zaten bilgili olan ancak tüm bu konular hakkında kafası karışık olan, başlangıç ​​düzeyindeki Scala3 geliştiricileri için hazırlanmıştır.implicits' ve koddaki parametreli özellikler.

Bu belgede bunun neden, nasıl, nerede ve ne zaman olduğu açıklanmaktadır. Tip Sınıfları (TC).

Bu belgeyi okuduktan sonra, Scala3'e yeni başlayan geliştiriciler, kullanmak için sağlam bilgi sahibi olacak ve kaynak kodunu derinlemesine inceleyeceklerdir. çok Scala kütüphanelerini kullanın ve deyimsel Scala kodu yazmaya başlayın.

Nedeniyle başlayalım…

İfade sorunu

1998 olarak, Philip Wadler belirtti “İfade problemi eski bir problemin yeni adıdır”. Yazılımın genişletilebilirliği sorunudur. Bay Wadler'in yazısına göre ifade sorununun çözümü aşağıdaki kurallara uygun olmalıdır:

  • Kural 1: Uygulanmasına izin ver mevcut davranışlar (Scala özelliğini düşünün) uygulanacak yeni temsiller (bir vaka sınıfını düşünün)
  • Kural 2: Şunun uygulanmasına izin verin: yeni davranışlar uygulanacak mevcut temsiller
  • Kural 3: Tehlikeye sokmamalı tip güvenliği
  • Kural 4: Yeniden derlemeyi gerektirmemelidir mevcut kod

Bu sorunu çözmek bu makalenin gümüş ipliği olacaktır.

Kural 1: Mevcut davranışın yeni temsile uygulanması

Herhangi bir nesne yönelimli dilin kural 1 için yerleşik bir çözümü vardır. alt tip polimorfizmi. Herhangi bir `güvenle uygulayabilirsiniztrait` bir bağımlılıkla tanımlandı `class` bağımlılığı yeniden derlemeden kendi kodunuzda. Bunu çalışırken görelim:

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()

Bu hayali örnekte, kütüphane `Lib1' (satır 5) bir özelliği tanımlar 'Blockchain' (satır 6) ve bunun 2 uygulaması (satır 9 ve 12). `Lib1` bu belgenin TÜMÜNDE aynı kalacaktır (kural 4'ün uygulanması).

`Lib2` (satır 15) mevcut davranışı uygular`Blockchain'yeni bir sınıfta'Polkadot' (kural 1) tür güvenli (kural 3) bir şekilde, yeniden derlemeden'Lib1(kural 4). 

Kural 2: Mevcut gösterimlere uygulanacak yeni davranışların uygulanması

'de hayal edelimLib2`yeni bir davranış istiyoruz`lastBlock`her biri için özel olarak uygulanacak`Blockchain`.

İlk akla gelen parametrenin türüne göre büyük bir switch oluşturmaktır.

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()))

Bu çözüm, halihazırda dilin içinde yer alan tür tabanlı polimorfizmin zayıf bir yeniden uygulamasıdır!

`Lib1` dokunulmadan bırakılır (bu belgenin her yerinde uygulanan kural 4'ü unutmayın). 

'de uygulanan çözümLib2`dır tamam 'de başka bir blockchain tanıtılıncaya kadarLib3'. Bu kod çalışma zamanında 3. satırda başarısız olduğundan tür güvenliği kuralını (kural 37) ihlal ediyor.Lib2' kural 4'ü ihlal eder.

Başka bir çözüm ise '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` dokunulmadan bırakılır (kural 4'ün tüm belgede uygulanması). 

`Lib2' türü için davranışı (satır 21) ve mevcut türler için 'uzantı'yı (satır 23 ve 25) tanımlar.

28-30. satırlar, yeni davranış her sınıfta kullanılabilir. 

Ancak bu yeni davranışı polimorfik olarak adlandırmanın bir yolu yoktur (satır 32). Bunu yapmaya yönelik herhangi bir girişim, derleme hatalarına (satır 33) veya tür tabanlı anahtarlara yol açar. 

Bu Kural n°2 yanıltıcıdır. Bunu kendi polimorfizm tanımımızla ve 'uzatma' hilesiyle uygulamaya çalıştık. Ve bu tuhaftı.

adında eksik bir parça var geçici polimorfizm: Davranışın ve türün tanımlandığı her yerde, bir türe göre bir davranış uygulamasını güvenli bir şekilde gönderme yeteneği. Giriş Tip Sınıfı desen.

Tür Sınıfı modeli

Tip Sınıfı (kısaca TC) desen tarifinin 3 adımı vardır. 

  1. Yeni bir davranış tanımlayın
  2. Davranışı uygulayın
  3. Davranışı kullanın

Bir sonraki bölümde TC modelini en basit şekilde uyguluyorum. Ayrıntılı, hantal ve pratik değildir. Ancak bekleyin, bu uyarılar belgenin ilerleyen kısımlarında adım adım düzeltilecektir.

1. Yeni bir davranış tanımlayın
Scala

object Lib2:
  import Lib1.*

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

`Lib1' bir kez daha dokunulmadan bırakıldı.

Yeni davranış is TC bu özellik tarafından hayata geçirildi. Özellikte tanımlanan işlevler, o davranışın bazı yönlerini uygulamanın bir yoludur.

parametre `A`, davranışı uygulamak istediğimiz türü temsil eder; bunlar, `'in alt türleridirBlockchainbizim durumumuzda.

Bazı açıklamalar:

  • Gerekirse parametreli tür `A' Scala tipi sistem tarafından daha da kısıtlanabilir. Örneğin, `A'olmak'Blockchain`. 
  • Ayrıca, TC'nin içinde açıklanan çok daha fazla işlev olabilir.
  • Son olarak, her fonksiyonun çok daha fazla keyfi parametresi olabilir.

Ancak okunabilirlik adına işleri basit tutalım.

2. Davranışı uygulayın
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")

Her tür için yeni `LastBlock` davranış bekleniyorsa, bu davranışın belirli bir örneği vardır. 

`Ethereum`uygulama satırı 22'den hesaplanır`eth' örneği parametre olarak aktarıldı. 

'in uygulanmasıLastBlock'için'Bitcoin` 25. satır, yönetilmeyen bir GÇ ile uygulanır ve parametresini kullanmaz.

Yani, 'Lib2`yeni davranışı uygular`LastBlock'için'Lib1'sınıfları.

3. Davranışı kullanın
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))

Satır 30`useLastBlock` örneğini kullanıyor `A' ve 'LastBlock` bu örnek için tanımlanan davranış.

Satır 33`useLastBlock` örneğiyle çağrılırEthereum` ve `nin bir uygulamasıLastBlock' 'de tanımlandı'Lib2'. ' seçeneğinin herhangi bir alternatif uygulamasını geçmenin mümkün olduğunu unutmayın.LastBlock[A]' (düşün bağımlılık ekleme).

`useLastBlock' temsil (gerçek A) ile davranışı arasındaki yapıştırıcıdır. Veri ve davranış birbirinden ayrılmıştır ve bu, işlevsel programlamanın savunduğu şeydir.

Tartışma

İfade probleminin kurallarını özetleyelim:

  • Kural 1: Uygulanmasına izin ver mevcut davranışlar  uygulanacak yeni sınıflar
  • Kural 2: Şunun uygulanmasına izin verin: yeni davranışlar uygulanacak mevcut sınıflar
  • Kural 3: Tehlikeye sokmamalı tip güvenliği
  • Kural 4: Yeniden derlemeyi gerektirmemelidir mevcut kod

Kural 1, alt tip polimorfizmi ile kutudan çıktığı gibi çözülebilir.

Az önce sunulan TC modeli (önceki ekran görüntüsüne bakın) kural 2'yi çözüyor. Tip açısından güvenlidir (kural 3) ve `'a hiç dokunmadıkLib1(kural 4). 

Ancak birkaç nedenden dolayı kullanılması pratik değildir:

  • 33-34. satırlarda davranışı açıkça örneği boyunca aktarmamız gerekiyor. Bu ekstra bir yüktür. Sadece şunu yazmalıyız:useLastBlock(Bitcoin())`.
  • Satır 31 sözdizimi nadirdir. Kısa ve daha nesne odaklı yazmayı tercih ederiz  `instance.lastBlock()' beyanı.

Pratik TC kullanımı için bazı Scala özelliklerini öne çıkaralım. 

Gelişmiş geliştirici deneyimi

Scala, TC'yi geliştiriciler için gerçekten keyifli bir deneyim haline getiren benzersiz bir dizi özelliğe ve sözdizimsel şekere sahiptir.

Örtükler

Örtük kapsam, belirli bir türün yalnızca bir örneğinin bulunabileceği, derleme zamanında çözümlenen özel bir kapsamdır. 

Bir program, bir örneği ` ile örtülü kapsama koyargiven' anahtar kelime. Alternatif olarak bir program, `anahtar sözcüğüyle örtülü kapsamdan bir örneği alabilir.using`.

Örtük kapsam derleme zamanında çözümlenir, bunu çalışma zamanında dinamik olarak değiştirmenin bilinen bir yolu vardır. Program derlenirse örtülü kapsam çözümlenir. Çalışma zamanında bunların kullanıldığı örtülü örneklerin eksik olması mümkün değildir. Olası tek karışıklık yanlış örtülü örneğin kullanılmasından kaynaklanabilir, ancak bu konu sandalye ile klavye arasındaki yaratığa bırakılmıştır.

Küresel kapsamdan farklıdır çünkü: 

  1. Bağlamsal olarak çözüldü. Bir programın iki konumu, örtülü kapsamda aynı türden bir örneği kullanabilir, ancak bu iki örnek farklı olabilir.
  2. Sahnenin arkasında kod, örtülü kullanıma ulaşılana kadar işlevin çalışması için örtülü argümanlar aktarıyor. Küresel bir hafıza alanı kullanmıyor.

Tip sınıfına geri dönüyoruz! Aynı örneği ele alalım.

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' daha önce tanımladığımız değiştirilmemiş kodun aynısıdır. 

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()))

Satır 19 yeni bir davranış `LastBlock' tam olarak daha önce yaptığımız gibi tanımlandı.

Satır 22 ve satır 25, `val`` ile değiştirildigiven'. Her iki uygulama da `LastBlock` örtülü kapsama konur.

Satır 31`useLastBlock`davranışı bildirir`LastBlockörtülü bir parametre olarak. Derleyici uygun ` örneğini çözerLastBlockArayanın konumlarından bağlamsallaştırılmış örtülü kapsamdan (33 ve 34. satırlar). Satır 28, 'den her şeyi içe aktarıyorLib2`, örtülü kapsam dahil. Böylece derleyici, 22 ve 25 numaralı satırlarda tanımlanan örnekleri 'nin son parametresi olarak iletir.useLastBlock`. 

Bir kütüphane kullanıcısı olarak tip sınıfını kullanmak eskisinden daha kolaydır. 34. ve 35. satırlarda geliştiricinin yalnızca davranışın bir örneğinin örtülü kapsama dahil edildiğinden emin olması gerekir (ve bu yalnızca bir 'import`). Eğer örtülü bir "değilse"given`kod nerede`usingDerleyici ona bunu söyler.

Scala'nın örtülü özelliği, sınıf örneklerini davranış örnekleriyle birlikte aktarma görevini kolaylaştırır.

Örtülü şekerler

Önceki kodun 22. ve 25. satırları daha da geliştirilebilir! TC uygulamalarını tekrarlayalım.

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. ve 25. satırlar, örneğin adı kullanılmıyorsa atlanabilir.

Scala


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

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

22 ve 25. satırlar, tipin tekrarı ` ile değiştirilebilirwith' anahtar kelime.

Scala

given LastBlock[Ethereum] = _.lastBlock

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

İçinde tek bir işlev bulunan dejenere bir özellik kullandığımız için IDE, kodu bir SAM ifadesiyle basitleştirmeyi önerebilir. Doğru olmasına rağmen, gelişigüzel kod golfü oynamadığınız sürece bunun SAM'in doğru bir kullanımı olduğunu düşünmüyorum.

Scala, gereksiz adlandırma, bildirim ve tür fazlalığını ortadan kaldırarak sözdizimini kolaylaştırmak için sözdizimsel şekerler sunar.

Uzatma

Akıllıca kullanıldığında, `extension' mekanizması, bir tür sınıfının kullanılmasına ilişkin sözdizimini basitleştirebilir.

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. satırlar genel bir genişletme yöntemidir.lastBlock`herhangi biri için tanımlanır`A'bir' ileLastBlock` Örtülü kapsamdaki TC parametresi.

33-34. satırlar, uzantı TC'yi kullanmak için nesne yönelimli bir sözdiziminden yararlanır.

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. satırda, tekrardan kaçınmak amacıyla TC parametresi tüm dahili hat için de tanımlanabilir. 30. satırda `'yi tanımlamak için uzantıdaki TC'yi yeniden kullanıyoruzpenultimateBlock` (` tarihinde uygulanabilmesine rağmen)LastBlock` doğrudan özellik)

Sihir, TC kullanıldığında gerçekleşir. İfade çok daha doğal hissettiriyor ve davranışın olduğu yanılsamasını veriyor.lastBlock' örneğiyle birleştirilmiştir.

TC'li jenerik tip
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))

Satır 34, işlev örtülü bir TC kullanır. Bu adın gereksiz olması durumunda TC'nin adlandırılmasına gerek olmadığını unutmayın.

TC modeli o kadar yaygın olarak kullanılmaktadır ki, “örtük davranışı olan bir türü” ifade etmek için genel bir tür sözdizimi vardır. 36. satır söz dizimi öncekine (34. satır) göre daha kısa bir alternatiftir. Adlandırılmamış örtülü TC parametresinin özel olarak bildirilmesini önler.

Bu, geliştirici deneyimi bölümünü sonlandırıyor. TC kullanıldığında ve tanımlandığında uzantıların, örtüklerin ve bazı sözdizimsel şekerlerin nasıl daha az karmaşık bir sözdizimi sağlayabildiğini gördük.

Otomatik türetme

Pek çok Scala kütüphanesi TC'yi kullanır ve programcının bunları kendi kod tabanlarında uygulamasına izin verir.

Örneğin Circe (bir json seri durumdan çıkarma kütüphanesi) TC `'yi kullanıyorEncoder[T]` ve `Decoder[T]Programcıların kod tabanlarında uygulamaları için. Bir kez uygulandığında kütüphanenin tüm kapsamı kullanılabilir. 

TC'nin bu uygulamaları çoğu zaman veri odaklı haritacılar. Herhangi bir iş mantığına ihtiyaç duymazlar, yazmaları sıkıcıdır ve vaka sınıflarıyla senkronize olarak sürdürülmeleri bir yüktür.

Böyle bir durumda, bu kütüphaneler denilen şeyi sunarlar. otomatik türetme veya Yarı otomatik türetme. Örneğin Circe'ye bakın otomatik ve Yarı otomatik türetme. Yarı otomatik türetme ile programcı bazı küçük söz dizimleriyle bir tür sınıfının örneğini bildirebilirken, otomatik türetme, içe aktarma dışında herhangi bir kod değişikliği gerektirmez.

Özet olarak derleme zamanında genel makrolar gözden geçirilir türleri saf veri yapısı olarak kullanın ve kütüphane kullanıcıları için bir TC[T] oluşturun. 

Genel olarak bir TC türetmek çok yaygındır; bu nedenle Scala, bu amaç için eksiksiz bir araç kutusu sundu. Bu yöntem, türetmeyi kullanmanın Scala 3 yolu olmasına rağmen, kütüphane belgelerinde her zaman tanıtılmaz.

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)

Hat 18 yeni bir TC`Named' tanıtıldı. Bu TC'nin blockchain işiyle kesinlikle alakası yoktur. Amacı, vaka sınıfının adına göre blok zincirini adlandırmaktır.

İlk önce 36-38. satırlardaki tanımlara odaklanın. TC türetmek için 2 sözdizimi vardır:

  1. 36. satırda TC örneği doğrudan vaka sınıfında ` ile tanımlanabilir.derives' anahtar kelime. Başlık altında derleyici belirli bir ` üretir.Named` örnek içinde `Polkadot'eşlik eden nesne.
  2. 37. ve 38. satırlarda, tip sınıflarının örnekleri, önceden var olan sınıflarda ` ile verilmiştir.TC.derived

31. satırda genel bir uzantı tanımlanır (önceki bölümlere bakın) ve `blockchainName' doğal olarak kullanılır.  

`derives` anahtar kelime formda bir yöntem bekliyor `inline def derived[T](using Mirror.Of[T]): TC[T] = ???` 24. satırda tanımlanmıştır. Kodun ne yaptığını derinlemesine açıklamayacağım. Geniş ana hatlarıyla:

  • `inline def` bir makroyu tanımlar
  • `Mirror` türleri iç gözlemlemek için kullanılan araç kutusunun bir parçasıdır. Farklı türde aynalar vardır ve kodun 26. satırı şuna odaklanır:Product` aynalar (bir vaka sınıfı bir üründür). Satır 27, eğer programcılar `` olmayan bir şey türetmeye çalışırsaProduct`, kod derlenmeyecek.
  • `Mirror` diğer türleri içerir. İçlerinden biri, ''MirrorLabel`, tür adını içeren bir dizedir. Bu değer, `'nin 29. satırının uygulanmasında kullanılır.NamedTC.

TC yazarları, belirli bir türe göre TC örneklerini genel olarak oluşturan işlevler sağlamak için meta programlamayı kullanabilir. Programcılar, kodları için örnekler oluşturmak amacıyla özel kitaplık API'sini veya Scala türetme araçlarını kullanabilir.

Bir TC'yi uygulamak için ister genel ister özel bir koda ihtiyacınız olsun, her durum için bir çözüm vardır. 

Tüm faydaların özeti

  • İfade problemini çözer
    • Yeni türler, geleneksel özellik kalıtımı yoluyla mevcut davranışları uygulayabilir
    • Mevcut türlere yeni davranışlar uygulanabilir
  • endişe ayrılığı
    • Kod karışık değildir ve kolayca silinebilir. Bir TC, işlevsel bir programlama sloganı olan verileri ve davranışı ayırır.
  • Güvenli
    • Yazım açısından güvenlidir çünkü iç gözleme dayanmaz. Türleri içeren büyük desen eşleşmesini önler. Böyle bir kod yazarken kendinizle karşılaşırsanız, TC kalıbının tam olarak uyacağı bir durumu tespit edebilirsiniz.
    • Örtülü mekanizma derleme açısından güvenlidir! Derleme zamanında bir örnek eksikse kod derlenmez. Çalışma zamanında sürpriz yok.
  • Geçici polimorfizm getirir
    • Geleneksel nesne yönelimli programlamada geçici polimorfizm genellikle eksiktir.
    • Geçici polimorfizm ile geliştiriciler, geleneksel alt yazma (kodu birleştiren) kullanmadan çeşitli ilgisiz türler için aynı davranışı uygulayabilirler.
  • Bağımlılık enjeksiyonu kolaylaştırıldı
    • Bir TC örneği Liskov ikame ilkesine göre değiştirilebilir. 
    • Bir bileşenin bir TC'ye bağımlılığı olduğunda, test amacıyla sahte bir TC kolayca enjekte edilebilir. 

Sayaç göstergeleri

Her çekiç bir dizi soruna yönelik tasarlanmıştır.

Tip Sınıfları davranış sorunlarına yöneliktir ve veri devralma için kullanılmamalıdır. Bu amaçla kompozisyonu kullanın.

Her zamanki alt tipleme daha basittir. Kod tabanına sahipseniz ve genişletilebilirliği hedeflemiyorsanız, tür sınıfları gereksiz olabilir.

Örneğin, Scala çekirdeğinde bir ` varNumeric` sınıf yazın:

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

Böyle bir tür sınıfının kullanılması gerçekten mantıklıdır çünkü cebirsel algoritmaların yalnızca Scala'ya gömülü türler (Int, BigInt, …) üzerinde değil, aynı zamanda kullanıcı tanımlı türler (a `) üzerinde de yeniden kullanılmasına izin verir.ComplexNumberörneğin).

Öte yandan, Scala koleksiyonlarının uygulanması çoğunlukla tip sınıfı yerine alt tiplemeyi kullanır. Bu tasarım birkaç nedenden dolayı anlamlıdır:

  • Koleksiyon API'sinin eksiksiz ve kararlı olması gerekiyor. Uygulamaların miras aldığı özellikler aracılığıyla ortak davranışları ortaya çıkarır. Yüksek oranda genişletilebilir olmak burada özel bir amaç değildir.
  • Kullanımı basit olmalıdır. TC, son kullanıcı programcının üzerine zihinsel bir yük ekler.
  • TC ayrıca performansta küçük bir ek yüke neden olabilir. Bu, bir koleksiyon API'si için kritik olabilir.
  • Ancak koleksiyon API'si, üçüncü taraf kitaplıklar tarafından tanımlanan yeni TC aracılığıyla hâlâ genişletilebilir.

Sonuç

TC'nin büyük bir sorunu çözen basit bir kalıp olduğunu gördük. Scala'nın zengin sözdizimi sayesinde TC modeli birçok şekilde uygulanabilir ve kullanılabilir. TC modeli, işlevsel programlama paradigmasıyla uyumludur ve temiz bir mimari için muhteşem bir araçtır. Sihirli bir değnek yoktur ve TC kalıbı uyduğu zaman mutlaka uygulanmalıdır.

Umarım bu belgeyi okuyarak bilgi edinmişsinizdir. 

Kod şurada mevcuttur: https://github.com/jprudent/type-class-article. Herhangi bir sorunuz veya yorumunuz varsa lütfen bana ulaşın. İsterseniz depodaki sorunları veya kod yorumlarını kullanabilirsiniz.


Jerome ihtiyatlı

Yazılım Mühendisi

spot_img

En Son İstihbarat

spot_img