Is there a way to derive a type from an existing one in Scala?
For example, for case class Person(name: String, age: Int)
I'd like to get a Product
/Tuple
of (Option[String], Option[Int])
, i.e. a type mapped from an existing one.
There's a feature in Typescript (mapped types) that allows this relatively easily, which is how I started thinking down this path. But I'm not sure how something like this would be done in Scala.
I feel like the solution involves using shapeless in some way but I'm not sure how to get there.
With Shapeless you can define type class
import shapeless.ops.{hlist, product, tuple}
import shapeless.poly.~>
import shapeless.{Generic, HList, Id, the}
trait Partial[A] {
type Out
}
object Partial {
type Aux[A, Out0] = Partial[A] { type Out = Out0 }
object optionPoly extends (Id ~> Option) {
override def apply[T](t: T): Option[T] = null
}
// implicit def mkPartial[A, L <: HList, L1 <: HList](implicit
// generic: Generic.Aux[A, L],
// mapper: hlist.Mapper.Aux[optionPoly.type, L, L1],
// tupler: hlist.Tupler[L1]): Aux[A, tupler.Out] = null
implicit def mkPartial[A, T](implicit
toTuple: product.ToTuple.Aux[A, T],
mapper: tuple.Mapper[T, optionPoly.type],
): Aux[A, mapper.Out] = null
}
and use it (the
is improved version of implicitly
)
case class Person(name: String, age: Int)
// val pp = the[Partial[Person]]
// type PersonPartial = pp.Out
type PersonPartial = the.`Partial[Person]`.Out
implicitly[PersonPartial =:= (Option[String], Option[Int])]
I'd suggest parameterize the type as follows:
case class Person[F[_]](name: F[String], age: F[Int])
And then you can derive types you want, like
import cats.Id
type IdPerson = Person[Id]
type OptPerson = Person[Option]
Where cats.Id
is simply defined as type Id[A] = A
. It is straightforward to write your own, but I suggest using cats' one since it comes with useful typeclass instances.