Search code examples
scalagenericstypestype-systems

Unwrap type variables


I am trying to "unwrap" a type variable from a generic type (without using reflection). E.g. in the case of an Option, the goal would be that the following (or similar) code compiles:

implicitly[OptionUnwrapper[Option[Int]] =:= Int]

I managed to come up with the following unwrapper:

trait OptionUnwrapper[T] {
  type Unwrap[_ <: Option[T]] = T
}

which can be used to as follows:

implicitly[OptionUnwrapper[Int]#Unwrap[Option[Int]] =:= Int]

The problem with this solutions is, that I have to declare the type (in my case Int) of interest, before it can be returned. Is there a way to leaf out this parameter like for instance in generic functions like:

def foo[T](x: Option[T]) = x.isDefined
foo[Int](Some(1)) // function call with unnecessary type parameter,
foo(Some(1))      // since compiler infers Int automatically

UPDATE 1 - Use Case:

In my actual use case, I want to receive a "tuple type" from a case class. i.e. lets say I have a case class

case class Person(name: (String, String), age: Int, hobbies: List[String])

I want something that yields the type of the unapplied Person. i.e.

type T = Tupled[Person] // T =:= Tuple3[Tuple2[String, String], Int, List[String]]

I tried to get to this point by using the type of the Person.unapply function, which would be

Function1[Person, Option[Tuple3[...]]]

From this point, the plan would be to "unwrap" the Function1[_, Option[X]] to X


Solution

  • (Note that I'm addressing the actual use case instead of the original "Unwrap type variables" question)

    As I said in a comment, If you are willing to use a macro, I have already provided a macro that fits the bill:

    How to do generic tuple -> case class conversion in Scala?

    You can then just do:

    val PersonFactory = CaseClassFactory[Person]
    type PersonTuple = PersonFactory.Tuple
    

    But given that you are also ready to put up with some boilerplate for each case class, you can also go for a macro-less solution:

    implicit class TupleTypeProvider[O,T](unapply: O => Option[T]) { type Tuple = T }
    def tupleTypeProvider[O,T](implicit p: TupleTypeProvider[O,T]) = p
    // Usage
    case class Person(name: (String, String), age: Int, hobbies: List[String])
    val personTupleInfo = tupleTypeProvider(Person.unapply _)
    type PersonTuple = personTupleInfo.Tuple
    // Let's check:
    implicitly[PersonTuple =:= Tuple3[Tuple2[String, String], Int, List[String]]]
    

    Or similarly:

    implicit class TupleTypeProvider[O,T](tupled: T => O) { type Tuple = T }
    def tupleTypeProvider[O,T](implicit p: TupleTypeProvider[O,T]) = p
    // Usage
    case class Person(name: (String, String), age: Int, hobbies: List[String])
    val personTupleInfo = tupleTypeProvider((Person.apply _).tupled)
    type PersonTuple = personTupleInfo.Tuple
    // Let's check:
    implicitly[PersonTuple =:= Tuple3[Tuple2[String, String], Int, List[String]]]