Post on 22-Sep-2019
Programmierenin
Scala
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 232 / 300
Motivation
• Aus Sicht unseres Sotwaretechnik-Lehrstuhls:Scala ist eine der derzeit modernsten Programmiersprachen
• KIV wird derzeit nach Scala portiert (fast fertig)
• KIV ist dann mit Scala und Java (GUI) programmiert.
• Scala unterstutzt Konzepte die vieles mit den Konzepten derKIV-Spezifikationen gemeinsam haben.
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 233 / 300
Was ist Scala?
• Scala ist entwickelt an der Uni Lausanne von Prof. Oderskyhttp://www.scala-lang.org
• Viele Dokus (Tutotial, etc.) kann man dort finden
• Eclipse-IDE: //http://www.scala-ide.org
• Scala ist eine objektorientierte Sprache
• Alle Konzepte von Java (Methoden, Klassen, Vererbung etc.) gibt es(z.T. in verbesserter Form) auch in Scala
• Scala wird in ByteCode der JVM compiliert
• Scala unterstutzt auch die Konzepte aus funktionalen Sprachen(Higher-Order Funktionen Pattern Matching etc.)
• Die Syntax von Scala ist etwas anders, und deutlich verbessert
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 234 / 300
Scala: Typen
• Scala kennt wie Java Klassen und generische Klassen
• Oberster Typ ist Any statt Object
• Scala kennt keine primitiven Typen:• statt int und Integer gibt es nur Int• analog: bool, Boolean ⇒ Boolean, array, Array ⇒ Array
• generische Typen werden mit eckigen Klammern geschrieben:Array[Int] statt Array<Int>
• Array-Zugriff mit runden Klammern a(i) (statt a[i])
• Fest vordefiniert sind Tupel mit (fur 3-Tupel) Typ (A,B,C). Siewerden auch als (a,b,c) konstruiert. Die Felder werden z.B. mit(a,b,c). 2 selektiert (liefert b)
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 235 / 300
Scala: Methodendefinition
• Scala kennt wie Java statische und dynamische Methoden
• Java:
type method(argty1 arg1, argty2 arg2, ...){
body
}
• Scala:
def method(arg1:argty1,arg2:argty, ...):type = {
body
}
• Der Typ void heisst Unit in Scala
• Methoden ohne Resultat konnen vereinfacht als
def method(arg1:argty1, ... ) { body }
geschrieben werden (kein Typ und kein Gleichheitszeichen)
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 236 / 300
Scala: Methodenaufruf
• Aufruf wie in Java fur statische und dynamische Methodentype . smethod (arg1, arg2, . . . )object . dmethod (arg1, arg2, . . . )
• Bei Methoden ohne Argumente durfen Leerklammern weggelassenwerden (auch schon bei der Definition)
• Konvention: Leerklammern weglassen gdw. keine Seiteneffekte:z.B. Selektoren (“getter”) und Tests: list.length, list.isEmpty
• Vorteil: Feldzugriff kann lokal auf get-Methode (gleichen Namens)geandert werden (keine Anderung in anderen Klassen!)
• Dynamischen Methoden mit einem Argument darf man mitobject method arg aufrufen (Infix: weder Punkt noch Klammern!)
• Vorteil: +, * etc. haben keine Sonderrolle mehr(sie konnen auch uberladen werden).
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 237 / 300
Ausprobieren von Scala
• Scala kann man wie Java compilieren und ein Programmmain(arglist:Array[String]):Unit in einem object ausfuhren.
• Scala kann aber auch mit einem Kommandozeileninterpreter(entweder von innerhalb Eclipse oder standalone) bedienen
• Aufruf von scala gibt scala>-prompt
• Eintippen von Ausdrucken wertet diese aus
scala> "Hello" + " World!"
"Hello World!"
scala> 3 + 4
7
scala> new Array(4,5).length
2
scala> new Array(5,6)(1)
6
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 238 / 300
Scala: Felder und lokale Variablen
• Scala unterscheidet bei Feldern und Variablen uberschreibbare (var)und nicht uberschreibbare (val; Java: final)
• nicht uberschreibare Felder/Variablen val x:Int = 5
• uberschreibare Felder/Variablen var x:Int = 5
• Scala implementiert eine Typinferenz:Fur die meisten Variablen und initialisierten Felder kann dieTypangabe wegfallen.
• Fur das obige Beispiel: val x = 5 ist ok
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 239 / 300
Scala: Methodenrumpfe
• Scala kennt keinen Unterschied zwischen Statement und Expression.
• Statements sind einfach Expressions vom Typ Unit
• Deshalb ist mischen erlaubt, z.B.:
val x = if (y > 5) { val y = 3; y + 2} else 5
• Der ?-Operator von Java ist in Scala uberflussig
• In Scala werden Strichpunkte nur benotigt, wenn zwei Statementsauf derselben Zeile stehen (sehr selten)
• Zeilenumbruch fugt (wo sinnvoll) implizit einen Strichpunkt ein
• Wenn return expr das letzte Statement einer Methode ist, darf nurexpr geschrieben werden.
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 240 / 300
Scala: Beispele fur Methoden
• Fakultatsfunktion:
def fac(x:Int):Int = {if (x == 0) 0 else fac(x - 1)}
• Lange (wie in Listenklasse vordefiniert):
def length:Int = {if (isEmpty) 0 else tail.length + 1}
• Letztere Funktion wurde in Java so aussehen:
int length() {if (isEmpty) return 0;
else return tail.length() + 1;}
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 241 / 300
Scala: Klassendefinitionen (1)
• Java Klassen enthalten sowohl statische als auch dynamischeMethoden
• Scala teilt die Methoden auf in dynamische in der Klasse undstatische, die im sog. companion object gleichen Namens stehen
• Beide mussen in dieselbe Datei, eines von beiden darf fehlen
• Wenn nur object ⇒ Singleton.
object A {
def staticmethod(arg:Int):Int = { 2 * arg }
}
class A {
val field = 5
def dynamicmethod(arg:Int):Int = { this.field + arg}
}
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 242 / 300
Scala: Klassendefinitionen (2)
• Java Klassen haben immer einen (haufig nutzlosen) nullstelligenKonstruktor
• meist muss einer definiert werden, der einfach nur Felder initialisiert.
• In Scala stattdessen: Felder, die im Konstruktor initialisiert werdensollen, als Argumente der Klasse. Kein nullstelliger Konstruktor.
class A(val field1:Int, var field2:Int) {...}
ergibt als moglichen Konstruktoraufruf new A(3,5). Ein nullstelligerKonstruktoraufruf ist nicht moglich.
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 243 / 300
Scala: Abstrakte Datentypen (1)
Scala unterstutzt freie Datentypen analog zu KIV-Specs.Beispiel: arithmetische Ausdrucke.
sealed abstract classed AExp
case class Binop(val e1:AExp, val str:String, val e2:AExp) extends AExp
case class Unop(val str:String, val e:AExp) extends AExp
case class Val(val v:Int) extends AExp
case class Var(val id:String) extends AExp
erlaubt zu schreiben:
Binop(Var("x"),"+",Unop("-",Val(4))))
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 244 / 300
Scala: Abstrakte Datentypen (2)
• Ein abstrakter Datentyp besteht aus einer abstrakten(Summen-)Klasse (AExp) und einer Anzahl Unterklassen(Summanden; hier Binop, Unop, Val, Id)
• Alle Klassen werden zusammen in die Datei AExpder Summenklasse geschrieben
• sealed ⇒ keine (weiteren) Unterklassen in anderen Dateien
• case class: erlaubt Konstruktoraufruf ohne Schlusselwort new(und Pattern matching; siehe spater)
• Die Felder sind meist nicht schreibbar (val),sie werden nur vom Konstruktor initialisiert(“immutable” datatype; entspricht algebraischen Datentypen).
• Gleichheit (i.e. ==) vergleicht case classes strukturell (nicht wie inJava auf Referenz-Gleichheit).
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 245 / 300
Scala: Listen (1)
Listen sind ein vordefinierter freier Datentyp
sealed abstract class List[+A]
case class ::[A](val head:A, tail:List[A]) extends List[A]
case object Nil extends List[Nothing]
erlaubt Konstruktion mit 1::2::Nil
Bem: eigentlich ::(1,::(2::Nil)), aber “::” ist auch eine Infixfunktion auf Listen, die mit
Doppelpunkt endet. Solche Infixfunktionen drehen die Argumentreihenfolge um!
Alternativ: List(1,2) (durch Aufruf der statischen Methode List mitvariabler Argumentzahl im companion object der Klasse List)
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 246 / 300
Scala: Listen (2)
• Listen erlauben kein Uberschreiben des head oder tail.
• Listen sind kovariant (das bedeuet das + vor dem Typparameter):jedes x:List[Int] ist auch ein x:List[Any]jedes x:List[Binop] ist auch ein x:List[AExp]
• allgemein x:List[type1] ist Subtyp von x:List[type2] falls type1 einSubtyp (Unterklasse) von type2 ist.
• Arrays sind dagegen nicht kovariant (weil modifizierbar)
• Nothing ist der leere Typ ohne jedes Element (Subtyp von jedemTyp). Nil ist deshalb vom Typ List[type] fur jeden Typ type
• Listen haben viele vordefinierte Funktionen, u.a. ++ (append), x(i)(get fur das i-te Element, 0-basiert); siehe scaladoc
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 247 / 300
Scala: Pattern Matching
• Scala erlaubt Pattern Matching fur ADTs• match verallgemeinert Java’s switch.• Methode allids (in Klasse AExp) sammelt
alle vorkommenden Variablen des Ausdrucks• Funktion ++ hangt Listen aneinander.• Ein Underscore steht fur wildcard (beliebig)
def allids:List[String] = {
this match {
case Var(id) => List(id)
case Binop(e1, ,e2) => e1.allids ++ e2.allids
case Unop( ,e0) => allids(e0)
case Val( ) => Nil
}
}
Binop(Var(”x”),”+”,Val(4)).allids ergibt List(”x”)
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 248 / 300
Scala: Higher-Order Funktionen
• Scala erlaubt Higher-Order Functions,i.e. Funktionen, die Funktionen als Argumente bekommen.
• Funktionstypen werden A => B geschrieben.
• Beispiel: Funktion map (in Klasse List[A] definiert)wendet eine Funktion f auf alle Listenelemente an
def map[B](f:A => B):List[B] = {
this match { Nil => Nil
x :: xs => f(x) :: xs.map(f)
}}
oder alternativ:
def map[B](f:A => B):List[B] = {
if (isEmpty) Nil else f(head) :: tail.map(f)
}
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 249 / 300
Scala: lambda-Abstraktionen
• Das Funktionsargument von map kann eine Funktion sein, die mitdef definiert wurde
• Da die Funktion meist nur einmal verwendet wird, gibt es auch dieMoglichkeit, sie als lambda-Abstraktion anzugeben
• x:Int => x + 1 ist die Funktion, die eins addiert
• (x:Int,y:Int) => x + y ist die Additionsfunktion
• Verwendung z.B mit map:List(1,2,3).map((x:Int) => 2 * x) ergibt List(2,4,6)
• Kurzschreibweise: e(_) statt (x:Int => e(x))
(manchmal ist Typangabe notwendig: e(_:Int)
• Beispiel: List((1,2),(4,5)).map(_._1) == List(1,4)
(mit Selektor . 1 fur Paare)
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 250 / 300
Scala: Datentyp Map
• Eine Map bildet endlich viele Schlussel (keys) auf Werte (values) ab.
• Analog zu Liste von Paaren, aber keine doppelten Schlussel
• Paare kann man sowohl mit (a,b) als auch mit a -> b konstruieren.
• Konstruktion aus Paaren mit Map("x" -> 3, "y" -> 4)
• Das letzte zahlt: Map("x" -> 3, "x" -> 4) == Map("x" -> 4)
• Zugriff uber Map("x" -> 3, "y" -> 4)("x") ergibt 3
• Addieren: Map("x" -> 3) + ("y" -> 4) ergibtMap("x" -> 3, "y" -> 4)("x")
• Loschen mit Map("x" -> 3, "y" -> 4) - "x" gibtMap("y" -> 4)
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 251 / 300
Scala: viele weitere Features
Scala hat noch viele andere Features, auf die wir hier nicht eingehen:
• lazy Funktionen und Felder
• interfaces erweitert scala zu traits.Diese durfen auch Code definieren.
• implizite Argumente
• selbstdefiniertes Pattern Matching
• erweiterte generische Syntax fur Schleifen
• . . .
17. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 252 / 300