Scala 3 is amazing, but currently it is cutting my project at the knees with its limitations after various features were deprecated and I am at lost (opened this thread after a brief discussion on gitter).
For years now I’ve been using Scala 2 to develop a DSL library for dataflow hardware description, called DFiant. I kept close track on the development of Scala 3, what was being deprecated and what was being added, and modified my library and language accordingly. Alas, now that I RC1 is out, and final verdict on various features is given, I currently see no way to continue my project in Scala.
The most relevant feature is macro annotations that are currently unavailable, but from what I now understand that even if they were, it wouldn’t be possible to do what I currently do with them. My basic macro annotation use-case is simple. @df class Foo extends DFDesign
transforms the class tree to add an implicit argument class Foo(implicit ctx : ContextOf[Foo]) extends DFDesign
. This is simple, but IIUC, Scala 3 will not allow this.
Before discussing alternatives, no, manually adding such an argument is unacceptable because:
- The audience this DSL caters to will not accept this.
- This is only the simple case. If we add type arguments this becomes a bit uglier.
Now why the different context? I could maybe have accepted a basic using Context
everywhere, but unfortunately that is not an option because implicit precedence rules prevent this. I’m using Context
for various things, and among those to keep ownership and unique meta sourcecode information (names, etc.) of the classes. Consider the following example:
import DFiant._
@df class ID extends DFDesign { //This is our `ID` dataflow design
val x = DFSInt(16) <> IN //The input port is a signed 16-bit integer
val y = DFSInt(16) <> OUT //The output port is a signed 16-bit integer
y := x //trivial direct input-to-output assignment
}
@df class IDTop extends DFDesign { //This is our `IDTop` dataflow design
val x = DFSInt(16) <> IN //The input port is a signed 16-bit integer
val y = DFSInt(16) <> OUT //The output port is a signed 16-bit integer
val id1 = new ID //First instance of the `ID` design
val id2 = new ID //Second instance of the `ID` design
id1.x <> x //Connecting parent input port to child input port
id1.y <> id2.x //Connecting sibling instance ports
id2.y <> y //Connecting parent output port to child output port
}
The classes represent the hierarchies and this information must be gathered somehow. Scala 3 has no DelayedInit
, so I chose to use a unique context approach that is generated for every class instantiation. If the context is not unique, then it will not be generated for every class because implicit precedence will choose the owner class argument over the inner implicit context.
Any suggestions how to continue (no, staying stuck at 2.xx forever is not an option)?
This worries me a lot. This is only the basic stuff. I didn’t even try yet to migrate all the macro madness.