Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to inject scala-code without exiting repl #2185

Open
bphenriques opened this issue Jun 3, 2023 · 9 comments
Open

How to inject scala-code without exiting repl #2185

bphenriques opened this issue Jun 3, 2023 · 9 comments

Comments

@bphenriques
Copy link

bphenriques commented Jun 3, 2023

Version(s) 1.0.0

Describe what needs to be done and why
I want to automate a repl that has several libs and imports by default.
My current ammonite setup:

object load {
  def fs2Version(version: String) = {
    repl.load.apply(s"""
      import $$ivy.`co.fs2::fs2-core:$version`
      import $$ivy.`co.fs2::fs2-reactive-streams:$version`
      import $$ivy.`co.fs2::fs2-io:$version`

      import cats.implicits._
      import cats.syntax.all._
      import cats.effect.{IO, Resource}
      import fs2.io.file.{Files, Path}
      import fs2.{Stream, text}

      // For unsafeRunSync
      implicit val runtime = cats.effect.unsafe.IORuntime.global
    """)
  }

  def fs2 = fs2Version("3.3.0")
}

Which makes it very easy to have a quick typelevel repl by calling load.fs2.

I am trying to replace ammonite for scala-cli, so I am exploring existing command line options for the same purpose, however the terminal exits right away.

~ ❯ echo 'import cats.implicits._; import cats.syntax.all._; import cats.effect.{IO, Resource}; import fs2.io.file.{Files, Path}; import fs2.{Stream, text}; implicit val runtime = cats.effect.unsafe.IORuntime.global' | scala-cli repl --scala 2 --toolkit typelevel:latest --interactive
Welcome to Scala 2.13.10 (OpenJDK 64-Bit Server VM, Java 19.0.1).
Type in expressions for evaluation. Or try :help.

scala> import cats.implicits._; import cats.syntax.all._; import cats.effect.{IO, Resource}; import fs2.io.file.{Files, Path}; import fs2.{Stream, text}import cats.implicits._
import cats.syntax.all._
import cats.effect.{IO, Resource}
import fs2.io.file.{Files, Path}
import fs2.{Stream, text}
val runtime: cats.effect.unsafe.IORuntime = IORuntime(cats.effect.unsafe.WorkStealingThreadPool@3dd750ba, cats.effect.unsafe.WorkStealingThreadPool@3dd750ba, IORuntimeConfig(512,1024,true,16,Duration.Inf,true,1 second,10 seconds,0.1))

scala> :quit

I am unsure if this is a bug, but it seems to be. I may be using scala-cli incorrectly, hence my request for feedback.

Is your feature request related to a past ticket or discussion?
AFAIK no.

Describe alternatives you've considered
It won't change much, but I considering moving the Scala code to a file and piping the file to scala-cli.

@MaciejG604 MaciejG604 added this to To do in Issue Board via automation Jun 5, 2023
@lwronski
Copy link
Contributor

lwronski commented Aug 8, 2023

Hi @bphenriques I sorry for the delayed response. I think I've found a potential solution to your issue. Try running the following command:

{ echo "import cats.implicits._; import cats.syntax.all._; import cats.effect.{IO, Resource}; import fs2.io.file.{Files, Path}; import fs2.{Stream, text}; implicit val runtime = cats.effect.unsafe.IORuntime.global"; cat; } | scala-cli repl --scala 2 --toolkit typelevel:latest

The trick here is using the cat command after the echo. This keeps the input stream open, allowing for interaction with the REPL later. However, I'm not sure if this will work with all shells.

@lwronski
Copy link
Contributor

Unfortunately, the above trick with cat works only with Scala 2. For Scala 3, it throws the following error:

$ { echo "import cats.implicits._; import cats.syntax.all._; import cats.effect.{IO, Resource}; import fs2.io.file.{Files, Path}; import fs2.{Stream, text}; implicit val runtime = cats.effect.unsafe.IORuntime.global"; cat; } | scala-cli repl  --toolkit typelevel:latest 

Exception in thread "main" java.lang.IllegalStateException: Unable to create a system terminal
	at org.jline.terminal.TerminalBuilder.doBuild(TerminalBuilder.java:323)
	at org.jline.terminal.TerminalBuilder.build(TerminalBuilder.java:265)
	at dotty.tools.repl.JLineTerminal.<init>(JLineTerminal.scala:25)
	at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:143)
	at dotty.tools.repl.ReplDriver.tryRunning(ReplDriver.scala:134)
	at dotty.tools.repl.Main$.main(Main.scala:7)
	at dotty.tools.repl.Main.main(Main.scala)

@bphenriques
Copy link
Author

@lwronski thank you! I will try out once I got back from my holidays and I will let you know

Intuitively, shouldn't --interactive allow me to keep "interacting" with the repl? Similar to how we run docker run -it <image> /bin/bash.

@bphenriques
Copy link
Author

bphenriques commented Aug 14, 2023

Attempted to run the Scala 2 version and it partially worked:

  • ✅ Can run IO(2).unsafeRunSync.
  • 🚫 I no longer have auto-complete nor colors for some reason. Specifically, "".<tab> leads to a actual \t.Is that expected or can it be my terminal setup?

Edit: Regarding Scala 3, I have the same output.

@SethTisue
Copy link
Contributor

@bphenriques inserting cat into the pipeline is presumably severing the connection with the terminal and thus disabling JLine

@bjornregnell
Copy link
Contributor

bjornregnell commented Aug 16, 2023

sbt can do this with something similar to this in your build.sbt:

Compile / console / initialCommands  := """
  import aCoolLibDepObjectOrPackage.*
  println("Up and running!")
"""

Now when you type console in sbt the REPL executes this stuff before you can continue with your REPL experiments, so it is possible also for Scala 3 to make this work already today without updating the compiler.

I think this is a very useful use case for scala-cli

@bphenriques
Copy link
Author

bphenriques commented Aug 16, 2023

The way I see, this ticket is somewhat conflating two different concerns unintentionally (my bad):

  1. Bug perhaps: --interactive should not close the terminal after piping Scala code
  2. Feature request: support running arbitrary code before running REPL (what Support for initial commands in REPL #604 talks about).

I would be content if 1. was addressed as it is good enough for me (I can create an wrapper) for it. I may have ideas for 2. but it might be preferable to discuss over the other issue given that it is an API concern - up to the maintainers.

@bjornregnell
Copy link
Contributor

@tgodzik Perhaps consider separating the to things by renaming this issue and open #604 again?

(I guess fixing --interactive not closing when piping from cat is tricky because of jline not started with forced "dumb" terminal?)

@lwronski
Copy link
Contributor

lwronski commented Aug 17, 2023

(I guess fixing --interactive not closing when piping from cat is tricky because of jline not started with forced "dumb" terminal?)

I also think that it's tricky. So, I believe it's reasonable to limit this issue to only adding support for initialCommands in REPL. This addresses real use cases of users. However, the trick with cat doesn't seem like a good solution to use.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants