Scala utiliza un enfoque extenso para dotar a las clases de funcionalidades adicionales llamadas clases de tipos. Para aquellos que nunca se han encontrado con este enfoque, les recomiendo leer este artículo . Este enfoque le permite mantener el código de algunos aspectos de la clase funcionando por separado de la implementación real de la clase. Y créelo sin siquiera tener acceso al código de la propia clase. En particular, este enfoque está justificado y se recomienda al dotar a las clases de la capacidad de serializar / deserializar en un formato específico. Por ejemplo, la biblioteca para trabajar con Json desde el marco de Play usa clases de tipos para establecer reglas para representar objetos en formato json.
Si la clase de tipo está destinada a utilizarse en una gran cantidad de clases diferentes (como en la serialización / deserialización), escribir el código de clase de tipo para cada clase con la que debería funcionar es irracional y laborioso. En muchos casos, puede generar una implementación de clase de tipo automáticamente conociendo el conjunto de atributos de la clase para la que está destinada. Desafortunadamente, en la versión actual de scala, la generación automática de la clase de tipo es difícil. Requiere que escriba macros usted mismo o utilice marcos de terceros para generar clases de tipos como sin forma o magnolia , que también se basan en macros.
Scala 3, que avanza rápidamente hacia el lanzamiento, tiene un lenguaje incorporado para la generación automática de clases de tipos. Este artículo intenta comprender el uso de este mecanismo utilizando una clase de tipo concreta como ejemplo.
Declaración de clase de tipo
Como ejemplo, usaremos una clase de tipo bastante artificial a la que llamaremos Inverter. Contendrá un método:
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)
Para tal clase, el valor debe invertirse, pero __hidden no debe invertirse:
inline given[T] (using m: Mirror.Of[T]): Inverter[T] = Inverter.derived[T]
println(summon[Inverter[Sample]].invert(Sample("abc","abc")))
// : Sample(cba,abc)
conclusiones
Como puede ver, la implementación incorporada de la generación de clases de tipos es bastante utilizable, bastante conveniente y cubre los patrones de uso básicos. Me parece que este mecanismo permitirá en la mayoría de los casos prescindir de macros y sin bibliotecas de terceros para generar clases de tipos.
El código fuente para el ejemplo final discutido en este artículo se puede reproducir aquí .