2

Scala 3's scala.deriving.Mirror has a type member MirroredElemLabels which is a tuple of string literals. What's the standard way to get that type as a value?

EDIT: here's the code that produces a compiler error from trying to use summonAll

case class Test(a: Int, b: String)
val mirror = implicitly[Mirror.ProductOf[Test]]
val labels = summonAll[mirror.MirroredElemLabels]
println(labels)

cannot reduce inline match with
 scrutinee:  compiletime.erasedValue[App.mirror.MirroredElemLabels] : App.mirror.MirroredElemLabels
 patterns :  case _:EmptyTuple
             case _:*:[t @ _, ts @ _]
kag0
  • 5,624
  • 7
  • 34
  • 67
  • http://dotty.epfl.ch/docs/reference/contextual/derivation.html https://stackoverflow.com/questions/62853337/how-to-access-parameter-list-of-case-class-in-a-dotty-macro/ – Dmytro Mitin Jul 08 '21 at 15:26
  • @DmytroMitin I had seen those docs, this is what I had reduced it down to https://scastie.scala-lang.org/4tfGCYJcSxafZr1mbmayEw but I'm not sure where to go from this compiler error – kag0 Jul 08 '21 at 17:20
  • `implicitly[Mirror.ProductOf[Test]]` is correct. But `summonAll[mirror.MirroredElemLabels]` is incorrect. It's true that `implicitly[mirror.MirroredElemLabels =:= ("i", "s")]` but it doesn't mean that there is an implicit of type `mirror.MirroredElemLabels` or implicits of singleton types `"i"`, `"s"`. This type `mirror.MirroredElemLabels` is a tuple of singleton types. And you probably want to find a value tuple consisting of values of these singleton types. – Dmytro Mitin Jul 09 '21 at 00:23

1 Answers1

3

Try to use scala.ValueOf

case class A(i: Int, s: String)

import scala.deriving.Mirror
import scala.compiletime.summonAll

val mirror = summon[Mirror.Of[A]]    
type ValueOfs = Tuple.Map[mirror.MirroredElemLabels, ValueOf]
val valueOfs = summonAll[ValueOfs]

def values(t: Tuple): Tuple = t match
  case (h: ValueOf[_]) *: t1 => h.value *: values(t1)
  case EmptyTuple => EmptyTuple

values(valueOfs) // (i,s)

We can now use scala.compiletime.constValueTuple

inline def constValueTuple[T <: Tuple]: T =
  (inline erasedValue[T] match
    case _: EmptyTuple => EmptyTuple
    case _: (t *: ts) => constValue[t] *: constValueTuple[ts]
  ).asInstanceOf[T]

and the return type will be precise

case class A(i: Int, s: String)

val mirror = summon[Mirror.Of[A]]

val res = constValueTuple[mirror.MirroredElemLabels] // (i,s)

res: ("i", "s") // compiles

Tested in 3.2.0

https://docs.scala-lang.org/scala3/reference/contextual/derivation.html

https://docs.scala-lang.org/scala3/reference/metaprogramming/compiletime-ops.html

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • One thing to note explicitly for anyone else who finds this: you need to use `summon` instead of `implicitly` for the mirror, or else it will not compile – kag0 Jul 13 '21 at 16:59
  • follow up question, is there a way to get `values` to return a correct type? This is what I'm working on so far https://scastie.scala-lang.org/dZfa6p7TRN66Ek2XcekNgw. It seems the compiler can't recognize equivalence of two match types – kag0 Jul 13 '21 at 22:12
  • The cheat way to get our final solution typed would be to just add `.asInstanceOf[mirror.MirroredElemLabels]` to the return of the call to `values` – kag0 Jul 14 '21 at 17:02
  • @kag0 Yeah, match types not always work well so far along with corresponding pattern matching on value level http://dotty.epfl.ch/docs/reference/new-types/match-types.html#dependent-typing For example calculations on type level along with calculations on value level can be done type-safely with good old type classes (as an alternative to match types). – Dmytro Mitin Jul 19 '21 at 16:43
  • @kag0 We can now use `scala.compiletime.constValueTuple` and the return type will be precise. See update. – Dmytro Mitin Oct 18 '22 at 04:20