My DSL's Scala 3 migration future is unclear. What to do?

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:

  1. The audience this DSL caters to will not accept this.
  2. 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
}

image

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.

7 Likes

As discussed on gitter, it seems that implementing Let traits with given clauses be implemented indirectly · Issue #7613 · lampepfl/dotty · GitHub would solve your problem, it’s too late to do that for 3.0 but could happen afterwards.

4 Likes

I would also love to have this ASAP.

1 Like