Scala verwendet einen umfassenden Ansatz, um Klassen mit zusätzlichen Funktionen auszustatten, die als Typklassen bezeichnet werden. Für diejenigen, die diesen Ansatz noch nie erlebt haben, empfehle ich, diesen Artikel zu lesen . Mit diesem Ansatz können Sie den Code einiger Aspekte der Funktionsweise der Klasse von der tatsächlichen Implementierung der Klasse trennen. Und erstellen Sie es, ohne Zugriff auf den Code der Klasse selbst zu haben. Dieser Ansatz ist insbesondere gerechtfertigt und wird empfohlen, wenn Klassen mit der Fähigkeit ausgestattet werden, in ein bestimmtes Format zu serialisieren / deserialisieren. Beispielsweise verwendet die Bibliothek für die Arbeit mit Json aus dem Play- Framework Typklassen, um Regeln für die Darstellung von Objekten im JSON-Format festzulegen .
Wenn die Typklasse in einer großen Anzahl verschiedener Klassen verwendet werden soll (z. B. bei der Serialisierung / Deserialisierung), ist das Schreiben des Typklassencodes für jede Klasse, mit der sie arbeiten soll, irrational und mühsam. In vielen Fällen können Sie eine Typklassenimplementierung automatisch generieren, indem Sie die Attribute der Klasse kennen, für die sie bestimmt ist. Leider ist in der aktuellen Version von Scala die automatische Generierung der Typklasse schwierig. Sie müssen entweder selbst Makros schreiben oder Frameworks von Drittanbietern verwenden, um Typklassen wie Shapeless oder Magnolia zu generieren , die ebenfalls auf Makros basieren.
Scala 3, das sich schnell in Richtung Release bewegt, verfügt über eine integrierte Sprache für die automatische Generierung von Typklassen. In diesem Artikel wird versucht, die Verwendung dieses Mechanismus anhand einer konkreten Typklasse als Beispiel zu verstehen.
Typklassendeklaration
Als Beispiel wird eine eher künstliche Typklasse verwendet, die wir Inverter nennen werden. Es wird eine Methode enthalten:
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)
Für eine solche Klasse muss der Wert invertiert werden, aber __hidden darf nicht invertiert werden:
inline given[T] (using m: Mirror.Of[T]): Inverter[T] = Inverter.derived[T]
println(summon[Inverter[Sample]].invert(Sample("abc","abc")))
// : Sample(cba,abc)
Schlussfolgerungen
Wie Sie sehen können, ist die integrierte Implementierung der Typklassengenerierung sehr benutzerfreundlich, sehr praktisch und deckt die grundlegenden Verwendungsmuster ab. Es scheint mir, dass dieser Mechanismus in den meisten Fällen auf Makros und auf Bibliotheken von Drittanbietern zum Generieren von Typklassen verzichten kann.
Sie können spielen , um mit dem Quellcode für das letzte Beispiel in diesem Artikel behandelt .