Scala usa uma abordagem abrangente para dotar classes com funcionalidades adicionais chamadas classes de tipo. Para aqueles que nunca encontraram essa abordagem, recomendo a leitura deste artigo . Essa abordagem permite que você mantenha o código de alguns aspectos do funcionamento da classe separadamente da implementação real da classe. E criá-lo sem nem mesmo ter acesso ao código da própria classe. Em particular, essa abordagem é justificada e recomendada ao dotar classes com a capacidade de serializar / desserializar em um formato específico. Por exemplo, a biblioteca Json da estrutura Play usa classes de tipo para definir as regras para representar objetos no formato json.
Se a classe de tipo se destina a ser usada em um grande número de classes diferentes (como em serialização / desserialização), escrever o código de classe de tipo para cada classe com a qual deve trabalhar é irracional e trabalhoso. Em muitos casos, você pode gerar uma implementação de classe de tipo sabendo automaticamente o conjunto de atributos da classe para a qual se destina. Infelizmente, na versão atual do scala, a geração automática da classe de tipo é difícil. Ele exige que você mesmo escreva macros ou use estruturas de terceiros para gerar classes de tipo, como sem forma ou magnólia , que também são baseadas em macro.
Scala 3, que está avançando rapidamente para o lançamento, possui uma linguagem integrada para geração automática de classes de tipo. Este artigo tenta entender o uso desse mecanismo usando uma classe de tipo concreto como exemplo.
Declaração de classe de tipo
Como exemplo, uma classe de tipo um tanto artificial será usada, que chamaremos de Inverter. Ele conterá um 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 classe, o valor deve ser invertido, mas __hidden não deve ser invertido:
inline given[T] (using m: Mirror.Of[T]): Inverter[T] = Inverter.derived[T]
println(summon[Inverter[Sample]].invert(Sample("abc","abc")))
// : Sample(cba,abc)
conclusões
Como você pode ver, a implementação embutida da geração da classe de tipo é bastante utilizável, bastante conveniente e cobre os padrões básicos de uso. Parece-me que este mecanismo permitirá, na maioria dos casos, fazer sem macros e sem bibliotecas de terceiros para gerar classe de tipo.
Você pode jogar ao redor com o código fonte para o exemplo final abordados neste artigo .