case class Cat(color: Int, isFat: Boolean)
case class Kitten(color: Int, isFat: Boolean)
I want to construct Kitten from Cat but want to do it automatically without passing all parameters (so if i add more field to Cat / Kitten I would not need to change code). Is there a compact way to achieve that in scala?
val kitten = Cat(1,True).what_to_do_here? // compact & immune to code changes no need to change that line in case of added fields.
You would want to take a look into Shapeless.
import shapeless._
import shapeless.syntax._
case class Cat(color: Int, isFat: Boolean)
case class Kitten(color: Int, isFat: Boolean)
val kitten = Kitten(2, true)
val genCat = Generic[Cat]
val genKit = Generic[Kitten]
val cat: Cat = genCat.from(genKit.to(kitten))
println(cat)
Shapeless is a library for generic programming. For example, the Generic typeclass can convert instances of any hierarchy of sealed traits and case classes (such as Cat
and Kitten
) into a generic representation (HLists and Coproducts), and back into any compatible class hierarchy. The generic representation in between can be manipulated in a type safe manner.
genKit.to(kitten)
takes a Kitten
, and produces a HList 2 :: true :: HNil
. Since that is compatible with the generic representation of Cat
without modification, it can be stored as such using genCat.from
.
In this case, Cat
and Kitten
are nearly identical. What if the order of types was different, or Kitten had an extra property? What if the name of each property is significant? There is tons of useful stuff in Shapeless to easily solve exactly these kind of situations by manipulating the generic representation. Take a look at this example, where some type Book is converted using LabelledGeneric (which uses a HLists with labels, a.k.a. Records), a property is added, and stored into ExtendedBook. All of this type-safe.
Shapeless does use some macros, but seemingly relies only on a minimal set of them. The user of Shapeless does not write any macros him/herself - the heavy lifting is done by Scala's powerful type system.
Assuming that the Cat
and Kitten
will have same fields, you can do
scala> case class Cat(color: Int, isFat: Boolean) defined class Cat scala> case class Kitten(color: Int, isFat: Boolean) defined class Kitten scala> Cat(1,true) res0: Cat = Cat(1,true) scala> Kitten.tupled(Cat.unapply(res0).get) // This line will remain same res1: Kitten = Kitten(1,true)scala> case class Cat(color: Int, isFat: Boolean, isBrown: Boolean) defined class Cat scala> case class Kitten(color: Int, isFat: Boolean, isBrown: Boolean) defined class Kitten scala> Kitten.tupled(Cat.unapply(Cat(2,true, false)).get) res3: Kitten = Kitten(2,true,false)
Obviously this will not work, if only one of the case classes gets changed arbitarily.
Should work, if both classes have same parameters. If one is changed, you will get compiler error.
case class Kitten(color: Int, isFat: Boolean)
case class Cat(color: Int, isFat: Boolean){
def toKitten = Cat.unapply(this).map(Kitten.tupled).get
}
val kitten = Cat(1,true).toKitten
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With