Scala menggunakan pendekatan ekstensif untuk memberkahi kelas dengan fungsionalitas tambahan yang disebut kelas tipe. Bagi mereka yang belum pernah menemukan pendekatan ini, saya sarankan membaca artikel ini . Pendekatan ini memungkinkan Anda untuk menyimpan kode dari beberapa aspek fungsi kelas secara terpisah dari implementasi kelas yang sebenarnya. Dan membuatnya bahkan tanpa memiliki akses ke kode kelas itu sendiri. Secara khusus, pendekatan ini dibenarkan dan direkomendasikan saat memberi kelas dengan kemampuan untuk membuat serial / deserialisasi ke dalam format tertentu. Misalnya, pustaka Json dari kerangka kerja Play menggunakan kelas tipe untuk menyetel aturan untuk merepresentasikan objek dalam format json.
Jika kelas tipe dimaksudkan untuk digunakan dalam sejumlah besar kelas yang berbeda (seperti dalam serialisasi / deserialisasi), maka menulis kode kelas tipe untuk setiap kelas yang harus bekerja tidak rasional dan melelahkan. Dalam banyak kasus, Anda bisa menghasilkan implementasi kelas tipe secara otomatis mengetahui himpunan atribut kelas yang dimaksudkan. Sayangnya, pada versi scala saat ini, pembuatan otomatis dari tipe sekelas sulit dilakukan. Ini mengharuskan Anda untuk menulis makro sendiri atau menggunakan kerangka kerja pihak ketiga untuk menghasilkan kelas tipe seperti tak berbentuk atau magnolia , yang juga berbasis makro.
Scala 3, yang dengan cepat bergerak menuju rilis, memiliki bahasa bawaan untuk pembuatan kelas tipe secara otomatis. Artikel ini mencoba untuk memahami penggunaan mekanisme ini menggunakan kelas tipe tertentu sebagai contoh.
Ketik deklarasi kelas
Sebagai contoh, kita akan menggunakan kelas tipe buatan yang akan kita sebut Inverter. Ini akan berisi satu metode:
trait Inverter[T] {
def invert(value: T): T
}
"" . , - , - NOT. . type class , , .
- type class . given ( implicit Scala 2) Inverter Inverter:
object Inverter {
given Inverter[String] = new Inverter[String] {
override def invert(str: String): String =
str.reverse
}
given Inverter[Int] = new Inverter[Int] {
override def invert(value: Int): Int =
-value
}
given Inverter[Boolean] = new Inverter[Boolean] {
override def invert(value: Boolean): Boolean =
!value
}
}
Inverter . derived[T] Inverter[T]. . type class ( shapeless 3). . derived Mirror.Of[T]. . Mirror.Of[T] :
case case
(enum enum cases)
sealed trait- case case .
type class .
runtime , compile time Scala 3 ( , , , inline).
derived . case ( ).
inline def derived[T](using m: Mirror.Of[T]): Inverter[T] = {
val elemInstances = summonAll[m.MirroredElemTypes]
inline m match {
case p: Mirror.ProductOf[T] =>
productInverter[T](p, elemInstances)
case s: Mirror.SumOf[T] => ???
}
}
inline def summonAll[T <: Tuple]: List[Inverter[_]] =
inline erasedValue[T] match
case _: EmptyTuple => List()
case _: (t *: ts) => summonInline[Inverter[t]] :: summonAll[ts]
. Miror.Of[T] MirroredElemTypes. case . , Inverter . summonAll. summonAll given summonInline. , summonAll type class .
Inverter - (case , case , ) (sealed trait enum). , productInverter, Inverter Inverter :
def productInverter[T](p: Mirror.ProductOf[T], elems: List[Inverter[_]]): Inverter[T] = {
new Inverter[T] {
def invert(value: T): T = {
val oldValues = value.asInstanceOf[Product].productIterator
val newValues = oldValues.zip(elems)
.map { case (value, inverter) =>
inverter.asInstanceOf[Inverter[Any]].invert(value)
}
.map(_.asInstanceOf[AnyRef])
.toArray
p.fromProduct(Tuple.fromArray(newValues))
}
}
}
. -, . trait Product, . - Inverter Inverter. , -, . fromProduct Mirror .
derived
derived type class . . - case derives type class. :
case class Sample(intValue: Int, stringValue: String, boolValue: Boolean) derives Inverter
Inverter[Sample] Sample. summon :
println(summon[Inverter[Sample]].invert(Sample(1, "abc", false)))
// : Sample(-1,cba,true)
type class .
, type class. given derived:
case class Sample(intValue: Int, stringValue: String, boolValue: Boolean)
@main def mainProc = {
given Inverter[Sample] = Inverter.derived
println(summon[Inverter[Sample]].invert(Sample(1, "abc", false)))
// : Sample(-1,cba,true)
}
type class . case type class . :
case class InnerSample(s: String)
case class OuterSample(inner: InnerSample)
type class:
given Inverter[InnerSample] = Inverter.derived
given Inverter[OuterSample] = Inverter.derived
println(summon[Inverter[OuterSample]].invert(OuterSample(InnerSample("abc"))))
// : OuterSample(InnerSample(cba))
type class Mirror.Of. given:
inline given[T] (using m: Mirror.Of[T]): Inverter[T] = Inverter.derived[T]
println(summon[Inverter[OuterSample]].invert(OuterSample(InnerSample("abc"))))
// : OuterSample(InnerSample(cba))
type class . trait , ( import ) , :
trait AutoInverting {
inline given[T] (using m: Mirror.Of[T]): Inverter[T] = Inverter.derived[T]
}
type class
type class type class . .
case :
case class SampleUnprotected(value: String)
case class SampleProtected(value: String)
case class Sample(prot: SampleProtected, unprot: SampleUnprotected)
SampleProtected Inverter, value. type class Sample:
inline given[T] (using m: Mirror.Of[T]): Inverter[T] = Inverter.derived[T]
given Inverter[SampleProtected] = new Inverter[SampleProtected] {
override def invert(prot: SampleProtected): SampleProtected = SampleProtected(prot.value)
}
println(summon[Inverter[Sample]].invert(Sample(SampleProtected("abc"), SampleUnprotected("abc"))))
// : Sample(SampleProtected(abc),SampleUnprotected(cba))
Inverter Sample Inverter SampleProtected. .
sealed trait enum
type class case ( ) type class sealed trait . derived , :
inline def derived[T](using m: Mirror.Of[T]): Inverter[T] = {
val elemInstances = summonAll[m.MirroredElemTypes]
inline m match {
case p: Mirror.ProductOf[T] =>
productInverter[T](p, elemInstances, getFields[p.MirroredElemLabels])
case s: Mirror.SumOf[T] =>
sumInverter(s, elemInstances)
}
}
def sumInverter[T](s: Mirror.SumOf[T], elems: List[Inverter[_]]): Inverter[T] = {
new Inverter[T] {
def invert(value: T): T = {
val index = s.ordinal(value)
elems(index).asInstanceOf[Inverter[Any]].invert(value).asInstanceOf[T]
}
}
}
. Mirror . ordinal Mirror. . Inverter ( ) .
. sealed trait Either Option:
def checkInverter[T](value: T)(using inverter: Inverter[T]): Unit = {
println(s"$value => ${inverter.invert(value)}")
}
@main def mainProc = {
inline given[T] (using m: Mirror.Of[T]): Inverter[T] = Inverter.derived[T]
given Inverter[SampleProtected] = new Inverter[SampleProtected] {
override def invert(prot: SampleProtected): SampleProtected = SampleProtected(prot.value)
}
val eitherSampleLeft: Either[SampleProtected, SampleUnprotected] = Left(SampleProtected("xyz"))
checkInverter(eitherSampleLeft)
// : Left(SampleProtected(xyz)) => Left(SampleProtected(xyz))
val eitherSampleRight: Either[SampleProtected, SampleUnprotected] = Right(SampleUnprotected("xyz"))
checkInverter(eitherSampleRight)
// : Right(SampleUnprotected(xyz)) => Right(SampleUnprotected(zyx))
val optionalValue: Option[String] = Some("123")
checkInverter(optionalValue)
// : Some(123) => Some(321)
val optionalValue2: Option[String] = None
checkInverter(optionalValue2)
// : None => None
checkInverter((6, "abc"))
// : (6,abc) => (-6,cba)
}
Inverter type class summon. Either ( SumOf, ProductOf).
type class , /. , . type class . Inverter : . . derived:
inline def derived[T](using m: Mirror.Of[T]): Inverter[T] = {
val elemInstances = summonAll[m.MirroredElemTypes]
inline m match {
case p: Mirror.ProductOf[T] =>
productInverter[T](p, elemInstances, getFields[p.MirroredElemLabels])
case s: Mirror.SumOf[T] =>
sumInverter(s, elemInstances)
}
}
inline def getFields[Fields <: Tuple]: List[String] =
inline erasedValue[Fields] match {
case _: (field *: fields) => constValue[field].toString :: getFields[fields]
case _ => List()
}
def productInverter[T](p: Mirror.ProductOf[T], elems: List[Inverter[_]], labels: Seq[String]): Inverter[T] = {
new Inverter[T] {
def invert(value: T): T = {
val newValues = value.asInstanceOf[Product].productIterator
.zip(elems).zip(labels)
.map { case ((value, inverter), label) =>
if (label.startsWith("__"))
value
else
inverter.asInstanceOf[Inverter[Any]].invert(value)
}
.map(_.asInstanceOf[AnyRef])
.toArray
p.fromProduct(Tuple.fromArray(newValues))
}
}
}
:
case class Sample(value: String, __hidden: String)
Untuk kelas seperti itu, nilai harus dibalik, tetapi __hidden tidak boleh dibalik:
inline given[T] (using m: Mirror.Of[T]): Inverter[T] = Inverter.derived[T]
println(summon[Inverter[Sample]].invert(Sample("abc","abc")))
// : Sample(cba,abc)
kesimpulan
Seperti yang Anda lihat, implementasi bawaan dari pembuatan kelas tipe cukup berguna, cukup nyaman dan mencakup pola penggunaan dasar. Tampaknya bagi saya bahwa mekanisme ini akan memungkinkan dalam banyak kasus untuk dilakukan tanpa makro dan tanpa pustaka pihak ketiga untuk menghasilkan kelas tipe.
Anda dapat bermain - main dengan kode sumber untuk contoh terakhir yang tercakup dalam artikel ini .