Home > database >  Does scala-3 macros support compile-time global variables? How to use them?
Does scala-3 macros support compile-time global variables? How to use them?

Time:11-22

A use case needs to compare the actual type of two type parameters at runtime time. That might be easily done with TypeTag, but it is deprecated in scala-3. Therefore I tried with TypeTest but it requires the existence of an instance of the type, which I don't have. So, I implemented TypeId, my own version of TypeTag. It is way way more limited and simple. It only supports type comparison and works only if a single instance of TypeId exists for each involved type.

import scala.collection.{mutable, immutable}

object TypeId {
    private var idSequencer: Int = 0

    private val typeIds: mutable.HashMap[Int, TypeId[?]] = mutable.HashMap.empty

    private def registerNew[A](tm: TypeId[A]): Int = this.synchronized {
        idSequencer  = 1
        typeIds.put(idSequencer, tm)
        idSequencer
    }

    def knownTypeIds: immutable.HashMap[Int, TypeId[?]] = this.synchronized(immutable.HashMap.from(typeIds))
}

trait TypeId[A] {
    val id: Int = registerNew(this)
    def equalsTo(that: TypeId[?]): Boolean = this.id == that.id
}

As you can see, it requires synchronization to be thread-safe, which is not convenient; and the value of knownTypeIds is vulnerable to race conditions.

I wonder if this is possible to create the unique ids at compile using a macro. I suppose that to do that I need some kind of compile-time global variable to implement the sequencer. Does scala-3 macros support compile time global variables or an alternative to achieve my goal?

EDIT: A couple of minutes after asking my question I came up with a very simple way to achieve my goal by making the identifier a string with the extended name of the type. Such a name can easily be generated in a macro. However, I leave the question to know if scala-3 macros support compile-time global variables.

CodePudding user response:

It would be a very bad idea, because of incremental compilation - it's quite hard to tell exactly what will be compiled or when. You could easily get something that works on a clean compile, but fails on partial re-compiles during development as not every bit of code that should be triggering the sequencer gets fired.

(I'm not sure if it's doable at all, and there might be other reasons to avoid it, but this is a general reason to avoid any sort of imperative code in macros.)

  • Related