Support upcoming Scala 3 indentation-based syntax in Scala 2.13

Not having to port your codebase to a new compiler.

Several Scala 2 things do not work as-is in Dotty, most notably scala-reflect macros. So it’s not like you can seamlessly try your Scala 2 code with Dotty.

3 Likes

For example, I might want to try out optional braces without swapping out my ecosystem.

Also, probably all my compiler flags are tuned for 2.13.

Now I really like Jorge’s proposal. I wonder how much is covered by “just syntax”?

3 Likes

Shouldn’t the syntax stabilize first? It might be meant for experimentation and feedback, but you’re going to have an even harder time getting that message across to all users when it’s merged into the mainstream Scala 2.13 compiler.

But the syntax is often the user interface for a language feature. So, supporting Scala 3’s syntax in Scala 2 basically means implementing Scala 3 in Scala 2. Example: type lambdas. What does it mean to support the [A] =>> ... syntax without actually supporting type lambdas?

What could be done though, would be to implement “pre-processors” or compiler plugins that would desugar Scala 3 syntax into Scala 2. In the case of type lambdas, this project already exists and is called kind-projector. I suggest opening an issue in that project to ask for support of Scala 3 [A] =>> ... syntax.

I suggest doing the same for other “syntactic” features (e.g., enums) you want to experiment with: implement your own code pre-processor or compiler plugin, but I’m afraid that the cost of re-implementing Scala 3 in the Scala 2 compiler codebase would be too high for the benefits.

My proposal is only concerned with the stylistic changes to the Scala 2 syntax, mostly related to the indentation-based syntax. Other syntax leveraged for new Scala 3 features can be assumed can be dropped.

That’s possible, but it’s not really what this proposal is about. I want an official implementation of the new Scala 3 syntax that I can use by just enabling a flag in the compiler.

I’d like the compiler team to be involved in implementing this feature because there is no official documentation explaining all the changes made in Scala 3’s syntax and I want the implementation to be a blessed, bug-free implementation of the new syntax.

That being said, I wouldn’t mind using a compiler plugin implementing the new syntax so long as it’s a faithful implementation of it. I just think that supporting it directly in the compiler will lower the barrier to trying it out.

1 Like

To add to that, implementing syntax pre-processors in scalac is not possible. kind-projector runs after parser and before namer. You can’t add a phase before the parser, the compiler implementation forbids it. The closest to making parsing “extensible” has been @Duhemm’s work on parser macros which aren’t sufficient either to implement Scala 3 syntax in Scala 2, so it looks like the only path forward is to implement it in the compiler itself behind a flag.

OK, this was not clear in your first post. I agree that this could be useful.

I’m not sure what would be the easiest way to achieve that: either a separate pre-processor tool converting from indentation-based to brace-based, or a compiler flag in Scala 2. We might also need a translation from brace-based to indentation-based, so that existing codebases could be converted and developers could see how the new style looks like on significant pieces of code.

That being said, I’m not sure whether we should first wait for the SIP committee to approve/reject the indentation-based syntax before we invest in creating these tools, or if we should first have this tool so that the SIP committee can decide.

In fact, type lambdas would be a prime candidate for this. The concept already exists, but currently has a horrible syntax. And as @jvican said type-projector cannot change Scala’s syntax.

Scala 3 syntax features that would be straightforward and useful to have with a compiler flag:

  • optional braces

  • enums

  • type lambdas

Anything else? given would be a higher-hanging fruit, but could probably be done too.

2 Likes

given is not just syntax. Its semantics are different as well.

It’s been a while I don’t look at enums but IIRC it’s not straightforward to map enums semantics to Scala 2. It if can be done I’m on board. As to given, I would probably leave it out.

1 Like

Maybe because of the Java interop part (or is there something else?). I’d argue it’s a minor part of the feature, and having pure Scala 2 enum syntax without any JVM magic would already be super useful.

I think that should do it:

// dotty
enum Color(val rgb: Int) {
  case Red   extends Color(0xFF0000)
  case Green extends Color(0x00FF00)
  case Blue  extends Color(0x0000FF)
}

// scala 2
trait EnumEntry {
  def name: String
  def ordinal: Int
}

trait Enum[A <: EnumEntry] {
  def values: Seq[A]
  def valueOf(name: String): Option[A] = values.find(_.name == name)
}

sealed abstract class Color(val name: String, val ordinal: Int) extends EnumEntry {
  // using 'def' just to emphasize that there could be methods here too
  def rgb: Int
}

object Color extends Enum[Color] {
  object Red extends Color("Red", 0) { val rgb: Int = 0xFF0000 }
  object Green extends Color("Green", 1) { val rgb: Int = 0x00FF00 }
  object Blue extends Color("Blue", 2) { val rgb: Int = 0x0000FF }
  override val values: Seq[Color] = Seq(Red, Green, Blue)
}

There’s also enumeratum, which might do exactly that using macros.

Is this possible to implement via a compiler plugin or is it too fundamental?

enumeratum does provide this functionality, based on my tinkering the primary difference is that implicits resolve against Scala 2 style enum values, but do not resolve against Scala 3 style enums.

I’m still undecided if this is a good or bad thing, but it is something to be aware of when doing this.

No, it’s not possible:

I think I want to intentionally scope this proposal very well and not include any syntactic changes related to new language features such as enums.

I’m mostly interested in exploring the indentation-based syntax. I see an opportunity here to to build momentum and try out a Python-like way of writing Scala 2.

This effort can be useful to help refine the existing syntax and spot corner cases that would only be found whenever people migrate to Scala 3 for good. I’m certainly most likely to migrate earlier to Scala 3 if my codebase already uses this style.

@odersky @lrytz Do you think this is a good idea?

1 Like

Momentum is good, running afoul of the sunken costs fallacy isn’t. Before large amounts of effort are put into this, it might be a good idea to wait on the discussion about this feature proposal that we’ve been promised.

Just like there is a segment of our community for which doesn’t want Scala to turn into Haskell, there’s a segment of our community that doesn’t want Scala to turn into Python.

2 Likes

This proposal only makes sense if the new syntax is finally merged and blessed in Scala 3. If it isn’t, then you can assume this won’t be implemented.

This thread merely suggests that the impact of the effort is extended to Scala 2.13.x so that the whole community can try it out and give feedback. I don’t intend to start another discussion about the syntax, I believe there are already other threads where that’s happening.

1 Like

To generalize a little, you are suggesting to backport a Scala 3 feature to 2.13 to give it more exposure. I see that this would be a practical way to test a new feature broadly.

However, this raises the question what features should be backported to 2.13 - enums were already suggested in this thread. Deciding what features are worth testing in this way is really non-obvious. I fear that the selection would be heavily biased by the feasibility, implementation complexity and risk for regressions.

There is also a chicken and egg problem: we want to backport a feature only once we know how it’s going to work in Scala 3. Many features I really wouldn’t want to backport to 2.13 right now, given the pace at which things still evolve in Dotty. But then it’s too late for testing and evaluating: it is now that we need to gather feedback about the new features, to get them finalized and decide if they’re viable.

I also have the feeling that backporting features would give a wrong incentive. Our goal should be to support projects in migrating and cross-compiling with Scala 3, to actually test Scala 3, the interaction of new features, the robustness of the new compiler, the quality of the backwards compatibility, etc.

5 Likes

Thanks for your detailed reply. I think maybe “back port” is too big of a word, but I understand where you’re coming from. The proposal here was to have experimental support under a flag only for the indentation-based syntax changes, so I intentionally drew a line here between this particular set of syntactic changes and the others because they are (a) easy enough to implement in the parser and (b) controversial enough that might warrant an exception to the rule of not backporting changes from Scala 3 and instead focus on the migration. This line might seem arbitrary and in a way it is… so yeah, I agree there might be other things worthier of attention.

3 Likes

For example, I might want to try out infix operators at the beginning of the line but the PR isn’t merged.

1 Like