Porting Ammonite to Dotty

This branch should work. It adds Scala 3 support relying on “dotty compatibility”, that is, some Ammonite modules are compiled with 2.13 while others are compiled with 3.x (and the resulting Ammonite compiles user code with 3.x). It only supports Scala 3.0.0-M1 for now, as it’s the only version that is both way compatible with Scala 2.13.4. Its README describes how to run a Scala 3 REPL from it.

I’d like to polish a few more things in it (expect some forced pushes), then open a PR for it.

Some testing would be welcome, yes! Feel free to toy with it, and report how it goes. Beware that some features are missing for now (namely, entrypoints in scripts, and type printing).

3 Likes

Cool, thanks! I’ll see if I can get it running this week.

2 Likes

I’m sorry, I haven’t made any progress with this yet. I went to the scala-3-with-dotty-compat branch, then did a git clone https://github.com/alexarchambault/Ammonite.git from there. Then I did:

cd Ammonite
git checkout scala-3-with-dotty-compat

In the readme.md file there (and on that web page) it says to use this command to build and run the REPL:

./mill -i -w amm[2.12.6].run

That worked, but I can’t create something like an enum in there. This also runs, but I can’t create an enum in it, either:

./mill -i -w amm[2.13.4].run

This does not work:

./mill -i -w amm[3.0.0-M1].run

After some poking around I found this older WIP Scala 3 support branch, which shows that you can use this command to run the Scala 3 REPL:

./mill -i 'amm.cross[3.0.0-M2].run'

I tried various incarnations of that command like these on the scala-3-with-dotty-compat branch, but they did not work:

./mill -i 'amm.cross[3.0.0-M1].run'
./mill -i 'amm.cross[3.0.0-M2].run'
./mill -i 'amm.cross[3.0.0-M3].run'
./mill -i 'amm.cross[2.12.6].run'
./mill -i 'amm.cross[2.13.4].run'

I also just looked at the scala-3.0.0-M3 branch, but that doesn’t show any different commands.

I also read the Scala decoupling PR, but I didn’t see a solution there.

I should add that I see this in the build.sc file:

val special3Version = "3"
val actual3Version = "3.0.0-M1"
val cross2_3Version = "2.13.4"

But I’m not sure how to use it.

I’m sorry I haven’t made any progress here, but am I anywhere near the right ballpark?

My bad, I removed the custom README for the PR. The command to run would be

./mill -i 'amm[3].run'

on the scala-3-with-dotty-compat branch

This should also work on the scala-3.0.0-M3 branch, giving you Scala 3.0.0-M3 rather than 3.0.0-M1.

1 Like

I just tested the M1 branch, and that works — thanks! I’ll try the M3 branch tomorrow. :+1:

2 Likes

hmm, the correct command should be:

./mill -i 'amm[3.0.0-M3].run'

It will emit some strange warnings but it works in the end

1 Like

I’m finally getting around to working with this. A lot of things work, so that’s cool, thanks. (Also, thanks for Coursier!)

Just working through the Scala 2 docs, what works:

  • pretty-printed output
    • a little different than the docs, but okay
  • io.Source.fromFile("foo") does not compile, but scala.io.Source.fromFile("foo") compiles and throws an exception as expected
  • creating an enum
  • creating a multiline { ... } block
  • various incantations of import $file work
  • import ivy
  • time
  • save/load
  • autocomplete works great with "foo".[tab] (i like that detailed output)
  • repl.prompt() = ">"

What does not work:

  • source
  • desugar
  • autocomplete did not work with Futu[tab]
  • Ctrl-C killed Ammonite (“Host JVM shutdown. Forcefully destroying subprocess …”)

I’ll test some more custom things tomorrow, especially more Scala 3 code.

5 Likes

Thanks for that!

Among what doesn’t work, we also have:

  • line numbers in errors are scrapped (which is a problem when compiling scripts in particular)
  • there’s no support for entrypoints in scripts (@main, …).
1 Like

About

I believe it originates from the way Ammonite is run from mill. It works for me when run slightly differently:

$ ./mill 'amm[3.0.0-M3].assembly'
$ ./out/amm/3.0.0-M3/assembly/dest/out.jar

I’ve had issues adding “deep completion” support (finding class names in not yet imported packages). Uncommenting these two lines should add them back, but the current implementation is way too slow for me.

1 Like

I just found that when I try this:

case class Circle(x: Double, y: Double, radius: Double)
extension (c: Circle)
  def diameter: Double = c.radius * 2

I get these errors:

repl> extension (c: Circle) 
(console)
  |extension (c: Circle)
  |                     ^
  |                     Extension without extension methods

repl>   def diameter: Double = c.radius * 2 
    |def diameter: Double = c.radius * 2
    |                       ^
    |                       Not found: c
Compilation Failed

I also tried pasting the extension method code inside curly braces, but that didn’t work either. Putting a method on the same line as extension also didn’t work.

Eventually I tried resetting the REPL, and got an NPE:

repl> repl.sess.load() 
java.lang.NullPointerException
  ammonite.$sess.cmd20$.<clinit>(cmd20.sc:51)

I also found that this sequence causes an NPE:

> ./mill -i 'amm[3.0.0-M3].run'
GITHUB REF None
fatal: no tag exactly matches '374bb7af5b105980db5eb3de59940c1474e4c61c'
[380/380] amm[3.0.0-M3].run 
Loading...
Welcome to the Ammonite Repl 0.8.3-856-374bb7af (Scala 3.0.0-M3 Java 11.0.9)
@ repl.sess.load() 
res0: SessionChanged = SessionChanged(removedImports = Set(), addedImports = Set(), removedJars = Set(), addedJars = Set())

@ remember 
   |val res1 = remember
   |           ^^^^^^^^
   |           Not found: remember
Compilation Failed

@ val remember = 42 
java.lang.NullPointerException

If these are bugs and you’d like me to report them somewhere, just let me know. Other things have been working, so this is cool to see! :+1:

(Followup) I don’t know if you’re interested in these details, but FWIW, other things that I tested today include these, and they all work:

  • the ~/.ammonite/predef.sc startup file
  • enums, with case classes, along with match and if expressions
  • importing a local .sc file
  • importing a local jar file
  • i didn’t initially know the syntax to import a jar file, so i found that everything related to using java.nio.file.{Path, Paths} works
1 Like

No worries on this. The default Scala REPL had a similar problem ~1-2 months ago, and when there’s a long delay, I don’t think the feature is worth it.

For anyone else interested in using this, here are the steps:

git clone https://github.com/alexarchambault/Ammonite.git
cd Ammonite
git checkout scala-3.0.0-M3
./mill -i 'amm[3.0.0-M3].run'
9 Likes