Bevezetés a Scalába

Kategória: Scala.

Áttekintés

A Scala funkcionális nyelv, ami azt jelenti, hogy a függvények a nyelv első osztályú elemei (first class citizen): egy változó értéke lehet függvény, egy függvény paramétere, sőt, visszatérési értéke is lehet függvény. A JVM-re épül; a lefordított bájtkód ugyanolyan class mint a Java esetén, és egymással kompatibilisek is. Tehát például az összes Java könyvtár Scala-ban is használható. A Scala kód többnyire jóval tömörebb a Java-hoz képest.

A fentiek alapján úgy tűnhet, hogy a Scala egyértelműen jobb a Java-nal. Ez ebben a formában nem igaz. A Scala-ban kijavították a Java sok hibáját, ugyanakkor bele is kerültek problémák. A nyelv nem igazán kiforrott, nagyon sok a bosszantó szarvashiba, amibe a kezdő programozó szinte törvényszerűen belefut; ezekről lesz szó részletesen. Az erőforrásigénye általában nagyobb a Java-nál, a tanulási görbe a Java-hoz képest is hosszabb (különösen az első lépések - látni fogjuk), a tömör kód pedig gyakran az olvashatóság rovására megy, különösen azoknál, akik hozzászoktak a Java szellős kódfelépítéséhez. A Scala minden tekintetben lassú: hosszú idő megtanulni, lassú a fordítása, lassan fut, lassan javítják benne a hibákat stb. A Scala nem ajánlott első nyelvként; megtanulása előtt érdemes megismerkedni a Java programozási nyelvvel.

Javasolt tananyagok:

Telepítés

A Scala telepítési filozófiája az is lehet, hogy "miért egyszerűsítenénk az életünket, ha bonyolítani is tudjuk?" Először a Java-t kell telepítenünk, a Java bevezetőben leírtak alapján. (Inkompatibilitási problémák lehetnek a legfrissebb Java-val; ez esetben érdemes egy korábbi stabil, pl. 1.8-as Java-t használni.) Majd jöhet a Scala. Letöltés: https://www.scala-lang.org/ → Download. Itt válasszuk ki a Download SBT-t (kb. egy PageDown), majd a Download the Scala binaries for Windows-t (kb. két PageDown), majd telepítsük először a Scala-t, utána az SBT-t. A Java-val ellentétben ez beállítja a szükséges környezeti változókat.

Interaktív mód

A Scala-t kétféleképpen használhatjuk: a Java-ban megszokott fordítás-futtatás mellett van interaktív mód is. Lássuk először ez utóbbit! Indítsuk el a Scala-t paraméterek nélkül így: scala, majd írjuk be az utasításokat, pl. print("Hello, world!")

>scala
Welcome to Scala 2.12.8 (Java HotSpot(TM) 64-Bit Server VM, Java 11.0.2).
Type in expressions for evaluation. Or try :help.

scala> print("Hello, world!")
Hello, world!
scala>

Telepítés nélkül is ki tudjuk próbálni pl. ezen az oldalon: https://scalafiddle.io/.

Fordító mód

Lássuk ugyanezt a hagyományos módszerrel! Hozzunk létre egy szöveges fájlt HelloWorld.scala néven a következő tartalommal:

object HelloWorld {
  def main(args: Array[String]) {
    print("Hello, world!")
  }
}

Fordítás:

scalac HelloWorld.scala

A fordítás eredménye a HelloWorld.class. Futtatás:

scala HelloWorld

Néhány megjegyzés:

  • Sokban hasonlít a Java-ra. Pl. itt is kapcsos zárójelet használunk a blokkokhoz, a fő függvény neve main.
  • A példában is látható a tömörítés. Az object azt jelenti, hogy egyből létrehoz egy példányt, így a static sem kell. Alapból minden publikus, így a public kulcsszóra sincs szükség. A visszatérési értéket megpróbálja a fordító kitalálni, így pl. a void is feleslegessé válik. A példában nem látszik, de ha egy metódusnak nem adjuk meg a törzsét, akkor rájön arra, hogy az absztrakt, így ezt sem kell kiírni. Sőt, a return is sok esetben feleslegessé válik, ugyanis az utoljára kiszámolt érték lesz a visszatérési érték.
  • A Scala-ban nem kötelező az osztálynév-fájlnév megfeleltetés, és ugyanez igaz a csomag-könyvtárstruktúra megfeleltetésre is.
  • Ha egy sorba egy utasítást teszünk (ami erősen javasolt, nemcsak itt, hanem mindegyik programozási nyelvben), akkor az utasítást követő lezáró pontosvessző sem kötelező. Majd látni fogjuk, hogy elég sok hasonló egyszerűsítést enged a Scala: pl. nem kötelező kitenni a függvényhívásnál a zárójelet, ha nincs paraméter, nem kötelező kitenni a pontot, ha egy objektum egyik elemére hivatkozunk stb.
  • Felmerül a kérdés, hogy működik-e a java HelloWorld, hiszen Java bájtkód az eredmény. Ebben a formában amiatt nem működik, mert nem találja a Scala standard könyvtárakat.
  • A formázásnál a tömörítés jegyében javasolt a két szóközös behúzás.

A kényszer megszűnésének jó részét egyébként személy szerint nem tartom jónak, mert az ilyen egyszerűsítések gyakran a kód olvashatóságának rovására történnek. Jobbnak tartom azt a kényszerítést, amit a Java alkalmaz.

sbt

A Scala készítője a Maven-t is túl bőbeszédűnek tartotta - tegyük hozzá, jogosan - és megalkotta az sbt névre keresztelt fordítóeszközt. Most készítsük el a "Helló, világ!" programot az sbt segítségével! Ahogy egyszerűsödik a folyamat, úgy bonyolódik; itt megfelelő könyvtárstruktúra kell:

  • src/main/scala/HelloWorld.scala: ide kerüljön a fenti program.
  • src/test/scala/: ide kerülnek az egységtesztek; egyelőre hagyjuk üresen.
  • build.sbt: ez írja le a fordítandót. A tartalma legyen ez:
name := "hello"
version := "1.0"
scalaVersion := "2.12.8"

Felesleges rész nem igazán van, és a függőségek kezelése is hasonlóan egyszerű, pl. libraryDependencies += "org.scalatest" %% "scalatest" % "1.9.1" % "test".

Majd jön a fordítás:

sbt compile

(Ha legalább 10-es Java-t használunk, akkor kiír egy figyelmeztetést; ennek az az oka, hogy lexikografikusan hasonlítja össze az 1.8-at az 1.10-zel, és így az 1-8-at újabbnak találja az 1.10-nél. Ilyen hibák sajnos vannak a Scala-ban, nem is kevés!)

Végül ha a fordítás sikeres volt (amihez internet kapcsolatra van szükség), akkor a következőképpen futtathatjuk:

sbt run

Hoppá, nem írt ki semmit! Ez ismét egy bosszantó hiba: a program vége előtt nem írja ki a pufferben felgyülemlett adatot (a kiírás ugyanis úgy történik, hogy nem írja ki azonnal, hanem a memóriában gyűjti az adatokat, és egyszerre többet ír ki). A forrásban írjuk át ezt: print erre: println. A rend kedvéért az eredmény:

object HelloWorld {
   def main(args: Array[String]) {
      println("Hello, world!")
   }
}

Itt fordítva és futtatva már kiírja az eredményt.

A fordítás eredménye egyébként ez (a megfelelő verzióval): sbt/target/scala-2.12/hello_2.12-1.0.jar, így a következő indítás is működik:

scala sbt/target/scala-2.12/hello_2.12-1.0.jar

Integrált fejlesztői környezet

Az integrált fejlesztői környezetek általában alapból nem támogatják a Scala-t, ahhoz ún. beépülőket (plugin) kell telepíteni. A Scala fejlesztők körében legelterjedtebb IDE az InteilliJ IDEA, melynek Scala beépülőjét ugyanaz a cég forgalmazza, amelyik magát az IDE-t is (JetBrains). Telepítése: File → Settings → Plugins → a keresőbe írjuk be, hogy Scala → Install. Vagy közvetlenül letölthetjük innen: https://plugins.jetbrains.com/plugin/1347-scala, és ez esetben a Plugins oldalon a kerékre kell kattintani a felső sorban és az Install Plugin from Disk…-et kiválasztani.

Ez se ment elsőre (meg másodikra, sőt, sokadikra sem). Az még tiszta sor, hogy File → New → Project… → Scala → sbt, majd az src/main/scala/ könyvtáron jobb kattintással a megfelelő elemet kiválasztva tudunk forrásfájlt létrehozni, a fordításkor viszont nálam végtelen ciklusba esett. Nálam az lett a megoldás, hogy kilépnem az IDEA-ból, lefordítottam konzolról, majd újból betöltöttem. Ekkor is meg kellett adni a Scala SDK-t, és egy újbóli teljes újrafordítás után működött.

Létezik egy Eclipse alapokon nyugvó Scala IDE, de - hogy, hogy nem - nálam nem indult. Mindenesetre ha valaki próbálkozni szeretne vele: http://scala-ide.org.

Szóval nem egy igazán kiforrott technológiáról van szó, rengeteg benne még a szarvashiba. De előre mutató, ráadásul hatással volt a Java-ra is, így érdemes megismerkedni vele.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License