Scala 3.2.0 : Illegal start of toplevel definition - scala

Scala people!
So i'm trying to learn scala, and whenever i use the interpreter, everything is fine. But if i try and code in a script and run it with the scala command, the same thing happens every time.
So this is a concrete problem i'm having, with an example from the book "Programming in Scala".
val greetStrings: Array[String] = new Array[String](3)
greetStrings(0) = "Hello "
greetStrings(1) = ", "
greetStrings(2) = "world"
for (i <- 0 to 2)
print(greetStrings(i))
gives me the error:
-- [E103] Syntax Error: /Users/USER/scala/pa.scala:2:0 -------------
2 |greetStrings(0) = "Hello "
|^^^^^^^^^^^^
|Illegal start of toplevel definition
|
| longer explanation available when compiling with `-explain`
-- [E103] Syntax Error: /Users/USER/scala/pa.scala:7:2 -------------
7 | print(greetStrings(i))
| ^^^^^
| Illegal start of toplevel definition
|
| longer explanation available when compiling with `-explain`
2 errors found
Error: Errors encountered during compilation
Actually whatever code i try to run, gives me this exact error.
For reference, i've never coded in java, so im not familiar with any particularities. I've mostly been coding in f# and python.

If you want to run it with scala (scala3), then you have to add a #main entry point:
#main def entryPoint() =
println("Hello, World")
If you insist on having statements on the top-level, use scala-cli.

Related

How to differentiate between a script and normal class files in Scala?

In the book, Programming in Scala 5th Edition, the author says the following for two classes:
Neither ChecksumAccumulator.scala nor Summer.scala are scripts, because they end in a definition. A script, by contrast, must end in a result expression.
The ChecksumAccumulator.scala is as follows:
import scala.collection.mutable
class CheckSumAccumulator:
private var sum = 0
def add(b: Byte): Unit = sum += b
def checksum(): Int = ~(sum & 0XFF) + 1
object CheckSumAccumulator:
private val cache = mutable.Map.empty[String, Int]
def calculate(s: String): Int =
if cache.contains(s) then
cache(s)
else
val acc = new CheckSumAccumulator
for c<-s do
acc.add((c >> 8).toByte)
acc.add(c.toByte)
val cs = acc.checksum()
cache += (s -> cs)
cs
whereas the Summer.scala is as follows:
import CheckSumAccumulator.calculate
object Summer:
def main(args: Array[String]): Unit =
for arg <- args do
println(arg + ": " + calculate(arg))
But when I run the Summer.scala file, I get a different error than what mentioned by the author:
➜ learning-scala git:(main) ./scala3-3.0.0-RC3/bin/scala Summer.scala
-- [E006] Not Found Error: /Users/avirals/dev/learning-scala/Summer.scala:1:7 --
1 |import CheckSumAccumulator.calculate
| ^^^^^^^^^^^^^^^^^^^
| Not found: CheckSumAccumulator
longer explanation available when compiling with `-explain`
1 error found
Error: Errors encountered during compilation
➜ learning-scala git:(main)
The author mentioned that the error would be around not having a result expression.
I also tried to compile CheckSumAccumulator only and then run Summer.scala as a script without compiling it:
➜ learning-scala git:(main) ./scala3-3.0.0-RC3/bin/scalac CheckSumAccumulator.scala
➜ learning-scala git:(main) ✗ ./scala3-3.0.0-RC3/bin/scala Summer.scala
<No output, given no input>
➜ learning-scala git:(main) ✗ ./scala3-3.0.0-RC3/bin/scala Summer.scala Summer of love
Summer: -121
of: -213
love: -182
It works.
Obviously, when I compile both, and then run Summer.scala, it works as expected. However, the differentiation of Summer.scala as a script vs normal file is unclear to me.
Let's start top-down...
The most regular way to compile Scala is to use a build tool like SBT/Maven/Mill/Gradle/etc. This build tool will help with a few things: downloading dependencies/libraries, downloading Scala compiler (optional), setting up CLASS_PATH and most importantly running scalac compiler and passing all flags to it. Additionally it can package compiled class files into JARs and other formats and do much more. Most relevant part is CP and compilation flags.
If you strip off the build tool you can compile your project by manually invoking scalac with all required arguments and making sure your working directory matches package structure, i.e. you are in the right directory. This can be tedious because you need to download all libraries manually and make sure they are on the class path.
So far build tool and manual compiler invocation are very similar to what you can also do in Java.
If you want to have an ah-hoc way of running some Scala code there are 2 options. scala let's you run scripts or REPL by simply compiling your uncompiled code before it executes it.
However, there are some caveats. Essentially REPL and shell scripts are the same - Scala wraps your code in some anonymous object and then runs it. This way you can write any expression without having to follow convention of using main function or App trait (which provides main). It will compile the script you are trying to run but will have no idea about imported classes. You can either compile them beforehand or make a large script that contains all code. Of course if it starts getting too large it's time to make a proper project.
So in a sense there is no such thing as script vs normal file because they both contain Scala code. The file you are running with scala is a script if it's an uncompiled code XXX.scala and "normal" compiled class XXX.class otherwise. If you ignore object wrapping I've mentioned above the rest is the same just different steps to compile and run them.
Here is the traditional 2.xxx scala runner code snippet with all possible options:
def runTarget(): Option[Throwable] = howToRun match {
case AsObject =>
ObjectRunner.runAndCatch(settings.classpathURLs, thingToRun, command.arguments)
case AsScript if isE =>
ScriptRunner(settings).runScriptText(combinedCode, thingToRun +: command.arguments)
case AsScript =>
ScriptRunner(settings).runScript(thingToRun, command.arguments)
case AsJar =>
JarRunner.runJar(settings, thingToRun, command.arguments)
case Error =>
None
case _ =>
// We start the repl when no arguments are given.
if (settings.Wconf.isDefault && settings.lint.isDefault) {
// If user is agnostic about -Wconf and -Xlint, enable -deprecation and -feature
settings.deprecation.value = true
settings.feature.value = true
}
val config = ShellConfig(settings)
new ILoop(config).run(settings)
None
}
This is what's getting invoked when you run scala.
In Dotty/Scala3 the idea is similar but split into multiple classes and classpath logic might be different: REPL, Script runner. Script runner invokes repl.

Trouble invoking command with quoted string using Scala's sys.process API

As can be seen in the following console session, the same command invoked from Scala produces different results than when run in the terminal.
~> scala
Welcome to Scala 2.12.6 (OpenJDK 64-Bit Server VM, Java 1.8.0_172).
Type in expressions for evaluation. Or try :help.
scala> import sys.process._
import sys.process._
scala> """emacsclient --eval '(+ 4 5)'""".!
*ERROR*: End of file during parsingres0: Int = 1
scala> :quit
~> emacsclient --eval '(+ 4 5)'
9
Has anyone encountered this issue and/or know of a work around?
I thought this may have been a library bug, so opened an issue as well: https://github.com/scala/bug/issues/10897
It seems that Scala's sys.process api doesn't support quoting. The following works: Seq("emacsclient", "--eval", "(+ 4 5)").!.

Different output depending on compilation with intellij or sbt

I have the following scala program that simply counts the words in a file:
package com.impatient
import java.util.Scanner
object Main extends App {
countWords()
def countWords(): Unit = {
val in = new Scanner(new java.io.File("C:\\tmp\\SampleText.txt"))
var wc = 0
while (in.hasNext()) {
var word = in.next()
wc += 1
println( wc + ". " + word)
}
in.close()
println("======================")
println(s"Total words: $wc")
}
def TestFunction(): Unit ={
println("Hello from test function!")
}
TestFunction()
}
When I execute this in intellij, either directly or via the sbt shell inside intellij, I get the following output:
mostly
positive:
======================
Total words: 309
Hello from test function!
So it correctly counts 309 words in the text file.
However if I try to execute the jar that I just compiled with intellij, directly, either via scala or sbt I get the following output:
λ scala .\impatientscala2_2.12-1.0.jar
======================
Total words: 0
Hello from test function!
So in this case, although the methods in Main are executed the wordcount is 0.
Why would be this the case? Is intellij referencing some other java libraries that standalone sbt or scala isn't? In that case why doesn't the Scanner fail?
Edit 1
I have cloned this repo to another PC to see what happens. And in that case I can observe the exact opposite, i.e.:
The program returns 0 word count when run from Intellij
When the jar is run directly with scala, it gives the current wordcount.
The 0 wordcount is due to not processing the while loop, as in.hasNext() returns nothing.
I found a workaround by using Paths.get from java.nio.file._.
//val in = new Scanner(new java.io.File("C:/tmp/SampleText.txt"))
val in: Scanner = new Scanner(Paths.get("C/tmp/SampleText.txt"))
This produces the correct jars on both PCs, when using Run | Sbt Task in Intellij, using SBT Shell in intellij, and using standalone SBT shell. If anyone can provide an explanation of why would java.io.File fail in certain setups that would be great.

How to start a Scala method from command line?

This question may sound a bit stupid, but I couldn't figure out, how to start a Scala method from the command line.
I compiled the following file Test.scala :
package example
object Test {
def print() {
println("Hello World")
}
}
with scalac Test.scala.
Then, I can run the method print with scala in two steps:
C:\Users\John\Scala\Examples>scala
Welcome to Scala version 2.9.2 (Java HotSpot(TM) Client VM, Java 1.6.0_32).
Type in expressions to have them evaluated.
Type :help for more information.
scala> example.Test.print
Hello World
But what I really like to do is, to run the method directly from the command line with one command like scala example.Test.print.
How can I achieve this goal ?
UPDATE:
Suggested solution by ArikG does not work for me - What I am missing ?
C:\Users\John\Scala\Examples>scala -e 'example.Test.print'
C:\Users\John\AppData\Local\Temp\scalacmd1874056752498579477.scala:1: error: u
nclosed character literal
'example.Test.print'
^
one error found
C:\Users\John\Scala\Examples>scala -e "example.Test.print"
C:\Users\John\AppData\Local\Temp\scalacmd1889443681948722298.scala:1: error: o
bject Test in package example cannot be accessed in package example
example.Test.print
^
one error found
where
C:\Users\John\Scala\Examples>dir example
Volume in drive C has no label.
Volume Serial Number is 4C49-8C7F
Directory of C:\Users\John\Scala\Examples\example
14.08.2012 12:14 <DIR> .
14.08.2012 12:14 <DIR> ..
14.08.2012 12:14 493 Test$.class
14.08.2012 12:14 530 Test.class
2 File(s) 1.023 bytes
2 Dir(s) 107.935.760.384 bytes free
UPDATE 2 - Possible SOLUTIONs:
As ArikG correctly suggested, with scala -e "import example.Test._; print" works well with Windows 7.
See answer of Daniel to get it work without the import statement
Let me expand on this solution a bit:
scala -e 'example.Test.print'
Instead, try:
scala -cp path-to-the-target-directory -e 'example.Test.print'
Where the target directory is the directory where scala used as destination for whatever it compiled. In your example, it is not C:\Users\John\Scala\Examples\example, but C:\Users\John\Scala\Examples. The directory example is where Scala will look for classes belonging to the package example.
This is why things did not work: it expected to find the package example under a directory example, but there were no such directory under the current directory in which you ran scala, and the classfiles that were present on the current directory were expected to be on the default package.
The best way to do this is to extend App which is a slightly special class (or at least DelayedInit which underlies it is):
package example
object Test extends App {
println("Hello World")
}
It's still possible to add methods to this as well, the body of the object is executed on startup.
Here you go:
scala -e 'example.Test.print'

Scala + stax compile problem during deploy process

I developed an app in scala-ide (eclipse plugin), no errors or warnings. Now I'm trying to deploy it to the stax cloud:
$ stax deploy
But it fails to compile it:
compile:
[scalac] Compiling 2 source files to /home/gleontiev/workspace/rss2lj/webapp/WEB-INF/classes
error: error while loading FlickrUtils, Scala signature FlickrUtils has wrong version
expected: 4.1
found: 5.0
/home/gleontiev/workspace/rss2lj/src/scala/example/snippet/DisplaySnippet.scala:8: error: com.folone.logic.FlickrUtils does not have a constructor
val dispatcher = new FlickrUtils("8196243#N02")
^
error: error while loading Photo, Scala signature Photo has wrong version
expected: 4.1
found: 5.0
/home/gleontiev/workspace/rss2lj/src/scala/example/snippet/DisplaySnippet.scala:9: error: value link is not a member of com.folone.logic.Photo
val linksGetter = (p:Photo) => p.link
^
/home/gleontiev/workspace/rss2lj/src/scala/example/snippet/DisplaySnippet.scala:15: error: com.folone.logic.FlickrUtils does not have a constructor
val dispatcher = new FlickrUtils("8196243#N02")
^
/home/gleontiev/workspace/rss2lj/src/scala/example/snippet/DisplaySnippet.scala:16: error: value medium1 is not a member of com.folone.logic.Photo
val picsGetter = (p:Photo) => p.medium1
^
/home/gleontiev/workspace/rss2lj/src/scala/example/snippet/RefreshSnippet.scala:12: error: com.folone.logic.FlickrUtils does not have a constructor
val dispatcher = new FlickrUtils("8196243#N02")
^
7 errors found
ERROR: : The following error occurred while executing this line:
/home/gleontiev/workspace/rss2lj/build.xml:61: Compile failed with 7 errors; see the compiler error output for details.
I see two errors, it is complaining about: the first one is FlickrUtils class constructor, which is defined like this:
class FlickrUtils(val userId : String) {
//...
}
The second one is the fact, that two fields are missing from Photo class, which is:
class Photo (val photoId:String, val userId:String, val secret:String, val server:String) {
private val _medium1 = "/sizes/m/in/photostream"
val link = "http://flickr.com/photos/" + userId + "/" + photoId
val medium1 = link + _medium1
}
Seems like stax sdk uses the wrong comliler (?). How do I make it use the right one? If it is not, what is the problem here, and what are some ways to resolve it?
Edit: $ scala -version says
Scala code runner version 2.8.0.final -- Copyright 2002-2010, LAMP/EPFL
I tried compiling everything with scalac manually, puting everything to their places, and running stax deploy afterwards -- same result.
I actually resolved this by moving FlickrUtils and Photo classes to the packages, where snippets originally are, but I still don't get, why it was not able to compile and use them from the other package.