How to execute a Scala3 script when system has both Scala2 and Scala3 installed? - scala

I want to execute a following script using Scala3:
#main def m() =
println("Hello, world! I'm a script")
When I type the command, scala hello.scala, I get the following error:
/Users/avirals/dev/learning-scala/hello-world/hello.scala:1: error: not found: type main
#main def m() =
^
one error found
I think it is because I have both the versions of Scala installed (2 and 3). I know how to start a REPL for both (as mentioned here) but I am not able to execute a Scala3 script from the command line.
[Update]
I tried scala3-repl hello.scala and it just opens the REPL:
➜ learning-scala git:(main) scala3-repl hello.scala
scala> m()
1 |m()
|^
|Not found: m
How do I execute a Scala 3 script from the command line given I have two different versions (2 and 3) of Scala installed?
My OS: MacOS
Update 2
As suggested in this answer, I tried running with amm and it worked for a few scripts. However, the following script failed:
Script:
#main def m(args: String*) =
var i = 0
while i < args.length do
println(args(i))
i += 1
Error:
➜ learning-scala git:(main) amm printargs.scala
printargs.scala:2:3 expected (If | While | Try | DoWhile | For | Throw | Return | ImplicitLambda | SmallerExprOrLambda)
var i = 0
^
Running the above script in a Scala3-REPL works:
➜ learning-scala git:(main) scala3-repl
scala> #main def m(args: String*) =
| var i = 0
| while i < args.length do
| println(args(i))
| i += 1
|
def m(args: String*): Unit
scala> m("aviral", "srivastava")
aviral
srivastava
Running the same script in a system (MacOS) that has only Scala3 installed works just fine as well.

There exists currently Minimal scripting support #11379. I was able to get it working by manually downloading a release from https://github.com/lampepfl/dotty/releases/download/3.0.0-RC3/scala3-3.0.0-RC3.zip, unzipping, and giving executable permission to launchers
./scala3-3.0.0-RC3/bin/scala hello.scala
Hello, world! I'm a script
With scala3-repl launcher you could at least do
$ scala3-repl
scala> :load hello.scala
def m(): Unit
scala> m()
Hello, world! I'm a script

Related

Scala 3: Using experimental types

The following scala 3 code following documentation works fine
import scala.compiletime.ops.string.*
#main def refinedTypeInAction: Unit =
val hello: "hello " + "world" = "hello world"
println(hello)
However, the one below
import scala.annotation.experimental
#experimental
object UseExperimental:
def x: scala.compiletime.ops.string.Substring["hamburger", 4, 8] = "urge"
throws compile time error as shown below
[error] -- [E008] Not Found Error: /Users/viswanath/projects/myproject/src/main/scala/RefinedType.scala:16:38
[error] 16 | def x: scala.compiletime.ops.string.Substring["hamburger", 4, 8] = "urge"
[error] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error] | type Substring is not a member of object scala.compiletime.ops.string
[error] one error found
I did refer to https://docs.scala-lang.org/scala3/reference/other-new-features/experimental-defs.html but I'm struggling to get past the compilation error. Please help!

Parsing scala 3 code from a String into Scala 3 AST at runtime

My goal is to get Scala 3 code as a String and to parse it into Abstract Syntax Tree for Scala 3 at runtime. In the process if the code has compilation errors, I should get that as part of some exception.
The larger goal is to end up with Expr[T] if the scala code is valid and execute it by splicing in the right bits(I have this part covered).
This was doable in Scala 2.* using scala-reflect here.
val source =
"""
|object HelloWorld {
| def main(args: Array[String]): Unit = {
| println("Hello, world!")
| }
|}
|
|HelloWorld.main(Array())
|""".stripMargin
val tree = toolbox.parse(source)
val binary = toolbox.compile(tree)
binary()
But as far as I can surmise, in Scala 3, scala-reflect will not be ported.
How could I achieve the same in Scala 3?
Some relevant links here and here
Ohh, you can look at ammonite: parser: https://github.com/com-lihaoyi/Ammonite/blob/master/amm/compiler/src/main/scala-3/ammonite/compiler/Parsers.scala
(They create a virtual file and run a compiler on it).
If you don't want evaluation but just AST, then maybe scalameta [https://scalameta.org/] will be enough. As I know, scala3 syntax is supported in the latest version, but scalameta itself (i.e. processing of parsed tree) is on scala2.

Print on Scala in Intellij and in ScalaFiddle

I am new in Scala. I try to repeat code from scaladocs.
I write
println(1 + 1)
and i cant run it.
But in ScalaFiddle (https://scalafiddle.io/) it works.
In other tutorial, i have to write
object ScalaApp{
def main(args: Array[String]): Unit = {
println(1 + 1)
}
}
and it works for me in Intellij.
What the difference?
Did you install IntelliJ's scala plugin?
If not, steps are detailled here : https://www.jetbrains.com/help/idea/run-debug-and-test-scala.html
On Unix you can also run simple scala programs using a shabang. For example with your code in test.sh you can
bash-3.2$ cat test.sh
#!/usr/bin/env scala
object ScalaApp{
def main(args: Array[String]): Unit = {
println(1 + 1)
}
}
bash-3.2$ chmod +x test.sh
bash-3.2$ ./test.sh
2

Run Scala application on different process

Hi I want to create a program that starts another application on a new process:
object A{
def main(args: Array[String]) {
Process(????).run
println("new process has been created")
}
}
the application that should run on the new process:
object B{
def main(args: Array[String]) {
print("Hello world")
}
}
I know I can run a script using Process(script_string_to_run), but I don't know how to make it run another application..
Run Scala program from another Scala program
Scala Program
B.scala
object B {
def main(args: Array[String]): Unit = println("hello world")
}
Now execute the above program like this from another Scala program.
scala> import sys.process._
import sys.process._
scala> "scala B.scala" !!
warning: there was one feature warning; re-run with -feature for details
res1: String =
"hello world
"
Note that the file name of the file is same as the object name containing main method. This way scala would recognise the main class/object and execute the main method of the program. If the name of the file is not the name of the object containing the main method then It would not know what to execute.
! gives the exit status of the program after execution whereas !! gives the output of the program.
scala> import sys.process._
import sys.process._
scala> val status = "scala B.scala" !
warning: there was one feature warning; re-run with -feature for details
hello world
status: Int = 0
scala> val output = "scala B.scala" !!
warning: there was one feature warning; re-run with -feature for details
output: String =
"hello world
Scala Script
Test.sh
#!/usr/bin/env scala
object B{
def main(args: Array[String]) {
print("Hello world")
}
}
B.main(Array[String]())
Now Just run the B program using
"./Test.sh" !!
Command Line Activity
➜ cd demo
➜ demo ./Test.sh
zsh: permission denied: ./Test.sh
➜ demo chmod +rwx Test.sh
➜ demo ./Test.sh
Hello world%
➜ demo ./Test.sh
Hello world%
➜ demo scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_45).
Type in expressions for evaluation. Or try :help.
scala> import sys.process._
import sys.process._
scala> "./Test.sh" !!
warning: there was one feature warning; re-run with -feature for details
res0: String =
"Hello world
"
scala> :q

How can I manage exception when using external processes in scala?

When running a external command, exceptions are simply displayed to the std output. How can I manage them from inside the code ?
Example :
import sys.process._
("yes -y 100" #| "head -c 1").!
I simply get the following stacktrace.
java.io.IOException: Pipe closed
at java.io.PipedInputStream.checkStateForReceive(PipedInputStream.java:261)
at java.io.PipedInputStream.awaitSpace(PipedInputStream.java:269)
at java.io.PipedInputStream.receive(PipedInputStream.java:232)
at java.io.PipedOutputStream.write(PipedOutputStream.java:149)
at scala.sys.process.BasicIO$.loop$1(BasicIO.scala:236)
at scala.sys.process.BasicIO$.transferFullyImpl(BasicIO.scala:242)
at scala.sys.process.BasicIO$.transferFully(BasicIO.scala:223)
at scala.sys.process.ProcessImpl$PipeThread.runloop(ProcessImpl.scala:159)
at scala.sys.process.ProcessImpl$PipeSource.run(ProcessImpl.scala:179)
Any try catch seems to be ignored.
Printing to stdout is what .! does. You will need to use .! with custom process logger, i.e.:
import scala.sys.process._
("yes -y 100" #| "head -c 1").!(new ProcessLogger {
override def buffer[T](f: => T): T = f
override def out(s: => String): Unit = ()
override def err(s: => String): Unit = ()
})
.... or just .!! to get a string