Coo LHibou
Coo LHibou

Reputation: 169

Type macro are dead : how can i compute a type from a definition?

Type macros are off.

However I have two important use cases that would have required them. The result is an important lost of extensibility in my application. Both are dynamic compile time generation of a type given other types. Basically i want to do something like (obviously not scala code but i think you'll get the idea) :

type T[U] = macro usecase1[U]

def usecase1[U]= U match {  
  case t if (t <:< Int) => String
  case ... => ...
}

Second use case is :

type Remaining[A, B >: A] = macro ...

where for example

class C
trait T1 extends C
trait T2 extends C

type Remaining[C with T1 with T2, T2] is assigned to "C with T1" at compile time
(so the macro would have generated the subclass list, and generated a new type from the list without T2) 

I didn't do it with macros so that are assumptions. I planned to do it now.. till i saw that type macro were dead. Anyway, did anyone knows a trick to obtain such functionalities? Thanks

Upvotes: 1

Views: 186

Answers (1)

George
George

Reputation: 8378

The first use case is indeed implementable with implicits to some degree, as far as I understand it. Here's an example of how this might look like:

scala> trait Bound[A] {
     |   type Type
     | }
defined trait Bound

scala> implicit val bound1 = new Bound[Int] { type Type = String }
bound1: Bound[Int]{type Type = String}

scala> implicit val bound2 = new Bound[String] { type Type = Double }
bound2: Bound[String]{type Type = Double}

scala> val tpe = implicitly[Bound[Int]]
tpe: Bound[Int] = $anon$1@2a6b3a99

scala> type U = tpe.Type
defined type alias U

But then:

scala> implicitly[U =:= String]
<console>:19: error: Cannot prove that U =:= String.
              implicitly[U =:= String]
                        ^

On the other hand:

scala> implicitly[bound1.Type =:= String]
res0: =:=[bound1.Type,String] = <function1>

Implicit resolution seems to be losing some types on the way. Not sure why, and how to work around that.


For the second use case, HLists immediately come to mind. Something like:

scala> trait Remaining[A <: HList, B] { type Result = Remove[A, B] }
defined trait Remaining

scala> new Remaining[C :: T1 :: T2 :: HNil, T2] {}
res5: Remaining[shapeless.::[C,shapeless.::[T1,shapeless.::[T2,shapeless.HNil]]],T2] = $anon$1@3072e54b

Not sure how to combine the resulting HList into a compound type though. Something like (pseudo-code):

trait Remaining[A <: HList, B] {
  def produceType(
    implicit ev0 : Remove.Aux[A, B, C],
             ev1 : IsCons.Aux[C, H, T],
             ev2 : LeftFolder[T, H, (T1, T2) => T1 with T2]) = ev2
             //                     ^ Not real syntax, type-level function to combine/mix types
  val tpe = produceType
  type Result = tpe.Out
}

Upvotes: 1

Related Questions