Załóżmy, że mamy następującą konfigurację:
import shapeless._, shapeless.labelled.{ FieldType, field }
case class Address(street: String, zip: Int)
case class Person(name: String, address: Address)
val person = Person("Jane", Address("street address", 12345))
type ShallowPersonRec =
FieldType[Witness.`'name`.T, String] ::
FieldType[Witness.`'address`.T, Address] :: HNil
type DeepPersonRec =
FieldType[Witness.`'name`.T, String] ::
FieldType[
Witness.`'address`.T,
FieldType[Witness.`'street`.T, String] ::
FieldType[Witness.`'zip`.T, Int] :: HNil
] :: HNil
Shapeless na LabelledGeneric
wspiera płytkie sprawę bezpośrednio:
val shallow: ShallowPersonRec = LabelledGeneric[Person].to(person)
Albo jeśli chcesz rodzajowe metody pomocnika:
def shallowRec[A](a: A)(implicit gen: LabelledGeneric[A]): gen.Repr = gen.to(a)
val shallow: ShallowPersonRec = shallowRec(person)
I możesz wrócić z from
:
scala> val originalPerson = LabelledGeneric[Person].from(shallow)
originalPerson: Person = Person(Jane,Address(street address,12345))
Głębokie sprawa jest bardziej skomplikowana, io ile wiem, nie ma wygodny sposób to zrobić z klas typu i innych narzędzi dostarczonych przez bezkształtne, ale można dostosować mój kod z this question (który jest teraz test case w Shapeless), aby robić to, co chcesz. Po pierwsze dla samego typu klasy:
trait DeepRec[L] extends DepFn1[L] {
type Out <: HList
def fromRec(out: Out): L
}
A potem wystąpienie o niskim priorytecie dla przypadku, gdy szef protokołu sam nie posiada LabelledGeneric
instancję:
trait LowPriorityDeepRec {
type Aux[L, Out0] = DeepRec[L] { type Out = Out0 }
implicit def hconsDeepRec0[H, T <: HList](implicit
tdr: Lazy[DeepRec[T]]
): Aux[H :: T, H :: tdr.value.Out] = new DeepRec[H :: T] {
type Out = H :: tdr.value.Out
def apply(in: H :: T): H :: tdr.value.Out = in.head :: tdr.value(in.tail)
def fromRec(out: H :: tdr.value.Out): H :: T =
out.head :: tdr.value.fromRec(out.tail)
}
}
a następnie resztę obiektu towarzyszącego:
object DeepRec extends LowPriorityDeepRec {
def toRec[A, Repr <: HList](a: A)(implicit
gen: LabelledGeneric.Aux[A, Repr],
rdr: DeepRec[Repr]
): rdr.Out = rdr(gen.to(a))
class ToCcPartiallyApplied[A, Repr](val gen: LabelledGeneric.Aux[A, Repr]) {
type Repr = gen.Repr
def from[Out0, Out1](out: Out0)(implicit
rdr: Aux[Repr, Out1],
eqv: Out0 =:= Out1
): A = gen.from(rdr.fromRec(eqv(out)))
}
def to[A](implicit
gen: LabelledGeneric[A]
): ToCcPartiallyApplied[A, gen.Repr] =
new ToCcPartiallyApplied[A, gen.Repr](gen)
implicit val hnilDeepRec: Aux[HNil, HNil] = new DeepRec[HNil] {
type Out = HNil
def apply(in: HNil): HNil = in
def fromRec(out: HNil): HNil = out
}
implicit def hconsDeepRec1[K <: Symbol, V, Repr <: HList, T <: HList](implicit
gen: LabelledGeneric.Aux[V, Repr],
hdr: Lazy[DeepRec[Repr]],
tdr: Lazy[DeepRec[T]]
): Aux[FieldType[K, V] :: T, FieldType[K, hdr.value.Out] :: tdr.value.Out] =
new DeepRec[FieldType[K, V] :: T] {
type Out = FieldType[K, hdr.value.Out] :: tdr.value.Out
def apply(
in: FieldType[K, V] :: T
): FieldType[K, hdr.value.Out] :: tdr.value.Out =
field[K](hdr.value(gen.to(in.head))) :: tdr.value(in.tail)
def fromRec(
out: FieldType[K, hdr.value.Out] :: tdr.value.Out
): FieldType[K, V] :: T =
field[K](gen.from(hdr.value.fromRec(out.head))) ::
tdr.value.fromRec(out.tail)
}
}
(. Zauważ, że DeepRec
cecha i obiekt musi być zdefiniowana razem być companioned)
To jest brudny, ale to działa:
scala> val deep: DeepPersonRec = DeepRec.toRec(person)
deep: DeepPersonRec = Jane :: (street address :: 12345 :: HNil) :: HNil
scala> val originalPerson = DeepRec.to[Person].from(deep)
originalPerson: Person = Person(Jane,Address(street address,12345))
to
/from
składnia do konwersji z powrotem do klasy przypadku jest konieczne, ponieważ dana płyta mogła odpowiadać na bardzo dużą liczbę potencjalnych klas przypadków, tak musimy mieć możliwość określenia typu celu, a ponieważ Scala nie obsługuje częściowo zastosowanych list parametrów, musimy podzielić operację na dwie części (z których jedna będzie miała wyraźnie określone jej typy, podczas gdy parametry typu dla drugiego zostanie wywnioskowane).