eirirlar eirirlar - 2 months ago 14
Scala Question

Avoid losing type info in return value

I'm trying to find a way to avoid losing type information in the return value for my method.

I have the following:

val defs0 = Default.mkDefault[Person, Some[String] :: Some[Int] :: HNil](Some("odd") :: Some(42) :: HNil)


Using IntelliJs "Add type annotation" gives the type:

Default.Aux[Person, ::[Some[String], ::[Some[Int], HNil]]]


This is fine, except I dont want to specify the fields of Person when I call mkDefault. So I created this:

object MkDefault {
object toSome extends Poly1 {
implicit def default[P] = at[P](Some(_))
}

def apply[P, L <: HList, D <: HList]
(p: P)
(implicit
lg: LabelledGeneric.Aux[P, L],
mpr: Mapper.Aux[toSome.type, L, D]
): Default.Aux[P, D] =
Default.mkDefault[P, D](mpr(lg.to(p)))
}


Now I can do:

val defs1 = MkDefault(Person("odd", 42))


Which is good, except the inferred type from IntellJ looks like this:

Default.Aux[Person, HNil]


How can I make the inferred type of defs1 equal to the inferred type of defs0?

*without having to specify the fields of class Person

Answer

Use mpr.Out instead of D as an output type:

object MkDefault {
  object toSome extends Poly1 {
    implicit def default[P] = at[P](Some(_))
  }

  def apply[P, L <: HList, D <: HList]
  (p: P)
  (implicit
   lg: LabelledGeneric.Aux[P, L],
   mpr: Mapper.Aux[toSome.type, L, D]
  ): Default.Aux[P, mpr.Out] =
    Default.mkDefault[P, D](mpr(lg.to(p)))
}

Example:

scala> val defs1 = MkDefault(Person("odd", 42))
defs1: shapeless.Default[Person]{type Out = shapeless.::[Some[String with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("name")],String]],shapeless.::[Some[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("age")],Int]],shapeless.HNil]]} = shapeless.Default$$anon$1@1f6a8bb8

P.S.

If you don't mind Option instead of Some, you can also use AsOptions

val opt = Default.AsOptions[Person]
val def3 = Default.mkDefault[Person, opt.Out](Some("aaa") :: Some(5) :: HNil)

Check:

scala> implicitly[def3.Out =:= ::[Option[String], ::[Option[Int], HNil]]]
res12: =:=[def3.Out,shapeless.::[Option[String],shapeless.::[Option[Int],shapeless.HNil]]] = <function1>