scala-cli icon indicating copy to clipboard operation
scala-cli copied to clipboard

Support for initial commands in REPL

Open lwronski opened this issue 3 years ago • 13 comments

Is your feature request related to a problem? Please describe. Support for running initial commands for REPL

Describe the solution you'd like

Adding a flag to specify initial commands. It can be supported in two ways:

  • accept string like this: "import Hello.*"
  • accept *.sc files which should be executed before opening REPL

lwronski avatar Feb 02 '22 12:02 lwronski

I'm not sure the Scala 3 REPL allows to specify initial code like this though (might be the same with the Scala 2 one).

alexarchambault avatar Feb 02 '22 13:02 alexarchambault

I am reading the documents for ammonite. It’s just that I don’t understand what initial commands are. Do you want scala-cli to support something like magic imports in ammonite ?

zmerr avatar Feb 03 '22 06:02 zmerr

You mean something like

scala-cli repl -ic “import Hello.*” or scala-cli repl -bo Hello.sc ?

zmerr avatar Feb 03 '22 06:02 zmerr

Hi @zmerr, I thinking about similar feature as in SBT.

Yea, It would be great if Scala CLI could be able to accept both -ic “import Hello.*” and scala-cli repl -bo Hello.sc

lwronski avatar Feb 03 '22 11:02 lwronski

Hi @lwronski so you mean using those commands for configuring scala-cli to import or open your preferred files in the current execution of repl or some other initial command in the string? Is it what you want me to implement?

zmerr avatar Feb 03 '22 11:02 zmerr

Don't know if that's what @lwronski is thinking about, but I think we'd want to use the --predef-code option of Ammonite (which is mentioned on its website). It basically allows to run a bit of code right before the repl session. I guess Scala CLI could accept such code via a flag like… --run-before maybe? or --predef-code like Ammonite?

For *.sc files, maybe we should either read them ourselves, and pass them to Ammonite via --predef-code too, or use Ammonite's --predef option (that accepts paths to *.sc files).

alexarchambault avatar Feb 03 '22 13:02 alexarchambault

@zmerr If you run scala-cli repl Hello.scala you have access to Hello object

VL-D-0317:scala-demo lwronski$ scala-cli repl Hello.scala 
Welcome to Scala 3.1.1 (17.0.1, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                
scala> println(Hello.hello)
Hello World

so it would be great if it worked like this:

VL-D-0317:scala-demo lwronski$ scala-cli repl Hello.scala --run-before "import Hello.*"
Welcome to Scala 3.1.1 (17.0.1, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                
scala> println(hello)
Hello World

if that's not possible for Scala 2/3 REPL, then we can add this option only for Ammonite REPL

VL-D-0317:scala-demo lwronski$ scala-cli repl Hello.scala  --ammonite --run-before "import Hello.*"
Welcome to the Ammonite Repl 2.5.1-6-5fce97fb (Scala 3.0.0 Java 17.0.1)
@ println(hello)
Hello World

lwronski avatar Feb 03 '22 14:02 lwronski

@lwronski Awesome. Now I got what it is that you actually want me to do. I am just curious, why are you still adding Hello.scala to the command, when you are already importing Hello.*? Sorry I am new to this.

zmerr avatar Feb 03 '22 15:02 zmerr

@lwronski I mean why not this:

scala-cli repl --ammonite --run-before "import Hello.*”

or

scala-cli repl --run-before "import Hello.*”

zmerr avatar Feb 03 '22 15:02 zmerr

If you run REPL without the source files, you have REPL without any loaded classes.

scala-cli repl --ammonite --run-before "import Hello.*” - it doesn't make sense, because there is no exists Hello object.

lwronski avatar Feb 04 '22 09:02 lwronski

@lwronski thanks. So making it work that way, requires making change to repl as well.

zmerr avatar Feb 04 '22 09:02 zmerr

In scala 3 if you write your init script like this

export Hello.*

and you run scala-cli repl InitScript.scala then in REPL you can already directly access everything that was defined in Hello. Does this solve our problem? The only thing I can think of that doesn't work out of the box currently is instant execution of side effects from scripts. So even if I write my init script like this

val xxx = println("Hello")

nothing will be printed until I type xxx in the REPL. But maybe that's actually good that starting a REPL doesn't cause any side effects under the hood?

prolativ avatar Mar 30 '22 11:03 prolativ

I've found one limitation of my solution: wildcard exports work correctly with objects but not with packages.

prolativ avatar Mar 31 '22 12:03 prolativ

@prolativ @Gedochao I tried to find among issues in dotty a discussion about export not working for packages. I think it really should, if possible, as I think that feature is very useful. Do you perhaps know where to find any potentially existing discussions about exporting top-level package members also, not just object members? This will allow init repl stuff to be used via scala-cli repl --scala-snippet as discussed in #1853

bjornregnell avatar Feb 14 '23 11:02 bjornregnell

To make things clear: named exports from packages do work - only wildcard exports don't. I assume that's because of complications wildcard imports from packages would bring to incremental compilation. E.g.:

// File1.scala

package pkg1

val x = "a"
// File2.scala

package pkg2

export pkg1.*

Now when this codebase gets compiled and a new file appears:

// File3.scala

package pkg1

val y = "b"

then the compiler would have to figure out that File2.scala has to be recompiled even though none of its dependencies known during the previous compilation has changed. Not to even mention potential cycles of dependencies. In case of objects its much simpler because they're closed - you cannot modify an object without modifying the file in which it's defined or without modifying the sources of its supertypes - but these are already tracked as dependencies transitively

prolativ avatar Feb 14 '23 14:02 prolativ

Thanks for the clear explanation @prolativ! So it's a trade-off between being restrictive or risk long compilation times.

I guess my view is that if I introduce wild-card package exports then I want it, even if compilation times get higher. It would be nice though if the compiler or build tool gave me a warning that wildcard exports of packages may trigger a complete re-compilation when new files are added and thus increase compile times.

bjornregnell avatar Feb 14 '23 15:02 bjornregnell

I would say it's not only about longer compilation times but also about correctness of code, unless we give up incremental compilation and compile everything from scratch each time, which is most likely not what we would like in general.

prolativ avatar Feb 14 '23 16:02 prolativ

Aha! That makes sense.

bjornregnell avatar Feb 14 '23 16:02 bjornregnell

It seems like most of the benefits of an initial script ran in the beginning of repl is already covered by the export syntax, with package wildcards being the problematic edge case. It'd be optimal for it to be covered by the compiler, but from what @prolativ wrote, it seems that won't happen (at least not soon).

Still, it'd be great to cover this edge case from Scala CLI's side, which would require supporting an initial script ran at the beginning of repl (which is different from --script-snippet, which adds a script to the repl's classpath). Unfortunately, it seems we lack the time to do it anytime soon. Community contribution is welcome, though!

Also, this could be a good argument for spending more time on improving the repl itself - if the repl accepted initial scripts, things would become easy.

Gedochao avatar Feb 22 '23 10:02 Gedochao

Thanks for the update @Gedochao ! Yes, ramping up the api of the REPL to make it more versatile would indeed be a nice thing! I think the Scala 3 REPL should have an api that let's it be embedded in an easy way, similar to how the Scala 2 REPL and also Ammonite for Scala 2 was easy to just instantiate and run things with a simple interpret method call. I had a quick look at the Scala 3 REPL api and it was not obvious how to do this as and I don't understand how to tap into its internal functional State in its current api... So my thumbs up for this!

bjornregnell avatar Feb 22 '23 11:02 bjornregnell

Closing as duplicate of https://github.com/VirtusLab/scala-cli/issues/604 with more recent discussion

tgodzik avatar Aug 16 '23 09:08 tgodzik

Is this a duplicate of itself :) or did you mean to link to another issue? @tgodzik

bjornregnell avatar Aug 16 '23 10:08 bjornregnell

I meant this https://github.com/VirtusLab/scala-cli/issues/2185 :sweat_smile:

tgodzik avatar Aug 16 '23 11:08 tgodzik

I'm not sure this is a duplicate of #2185 ; that issue is (also) about the --interactive option etc.

I think being on feature parity with the sbt setting initialCommands is important, as in:

console / initialCommands := """import stuff.*"""

The above works also for Scala 3.

The use case is to prevent tedious re-typing of lot's of imports in every repl session.

Should I open another issue or should we re-open this? I think it is important that this use case is not "forgotten".

Also it might be easier to solve allowing pre-REPL import rather than supporting all the stuff discussed in #2185 so a seperate issue for the import use-case might make sense?

bjornregnell avatar Feb 12 '24 17:02 bjornregnell

Should I open another issue or should we re-open this? I think it is important that this use case is not "forgotten".

Also it might be easier to solve allowing pre-REPL import rather than supporting all the stuff discussed in https://github.com/VirtusLab/scala-cli/issues/2185 so a seperate issue for the import use-case might make sense?

Agreed, I think a new issue makes sense, given the number of different things that were discussed here and in #2185.

Gedochao avatar Feb 13 '24 07:02 Gedochao

OK I'll make a new issue soon and link to the other issues.

bjornregnell avatar Feb 13 '24 08:02 bjornregnell