scalagenericsshapeless

Mapped types in Scala


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.


Solution

  • 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
    
      implicit def mkPartial[A, L <: HList, L1 <: HList](implicit
        generic: Generic.Aux[A, L],
        mapper: hlist.Mapped.Aux[L, Option, L1],
        tupler: hlist.Tupler[L1],
      ): Aux[A, tupler.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])]