scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.8k stars 1.04k forks source link

Reflection: no way to compute LUB or GLB of a TypeRepr #15809

Open neko-kai opened 2 years ago

neko-kai commented 2 years ago

Compiler version

3.1.2

Minimized code

inline def lubOf[T <: AnyKind]: String = ${ lubOfImpl[T] }

def lubOfImpl[T <: AnyKind: Type](using qctx: Quotes): Expr[String] = {
  import qctx.reflect.*
  '{ ${ TypeRepr.lub(List(TypeRepr.of[T])) } }
}

Output

value lub is not a member of qctx.reflect.TypeReprModule
    '{ ${ TypeRepr.lub(List(TypeRepr.of[T])) } }

Expectation

We need a way to compute the LUB of a set of types to port features of izumi-reflect to Scala 3

Original issue: https://github.com/zio/izumi-reflect/issues/133

nicolasstucki commented 1 year ago

The lub can be computed as follows types.foldLeft(TypeRepr.of[Nothing])(OrType).

sjrd commented 1 year ago

And likewise, the glb can be computed as types.foldLeft(TypeRef.of[Any])(AndType). ;)

neko-kai commented 1 year ago

@nicolasstucki @sjrd Technically correct, sure.

I mean the analogue of lub as in Scala 2's reflection API and in Scala 3's type inference - widened to drop unions. e.g. lub[Int, Long] = AnyVal

sjrd commented 1 year ago

The reflection API does not give access to type inference, because type inference is not specified and could therefore break at any time.

And outside of type inference, the lub/glb notion from Scala 2 has no equivalent/is irrelevant in Scala 3. It was necessary in Scala 2 because it did not have union and intersection types. In the Scala 3 type system, by definition, lub and glb are | and &.

neko-kai commented 1 year ago

@sjrd Well, in the case where I need lub I'm interacting with the Java type system, where such notions are still relevant. I'm extracting the common base type of an intersection type, then extracting its base class.

type inference is not specified and could therefore break at any time.

That's just an argument, if far-fetched, for having such a function - if you were splicing code that relied on current widened type inference, you'd want to compute types manually to make sure it still works if inference surfaces unions in the future.

Or if implicit search surfaces unions in the future. I'm using implicit search to compute non-union LUB in one part of library API, if implicit search were to surface unions in the future too, I'd like to still retain the ability to replace that part with a macro.