-
Notifications
You must be signed in to change notification settings - Fork 0
/
2_Scala.tex
306 lines (271 loc) · 8.51 KB
/
2_Scala.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
\section{Scala}
\begin{breakbox}
\textbf{Expression Problem:}\\
Wie kann man zu einem Programm \\
a) neue Datentypen \\
b) neue Operationen \\
hinzufuegen, ohne\\
1) bestehenden Code anzupassen\\
2) keine Typensicherheit zu verlieren \\\\
\emph{Loesung OO}
\begin{itemize}
\pro neue Datentypen mit Vererbung
\con neue Operationen, schwer da Hierarchie anzupassen. Loesung: Visitor
\end{itemize}
\emph{Loesung FP}
\begin{itemize}
\con neue Datentypen, schwer alle Pattern-Matches in Fkt anpassen. Loesung Haskell: Type Classes
\pro neue Operationen, einfach da getrennt von Daten
\end{itemize}
\end{breakbox}
\begin{breakbox}
\textbf{Types:}\\
\emph{Typisierung:} statisch mit type-inference\\
\emph{scala.Any:} \mintinline[fontsize=\scriptsize]{scala}{AnyVal, Anyref}\\
\emph{scala.AnyVal:} \mintinline[fontsize=\scriptsize]{scala}{Int, Float, Boolean, Nothing}\\
\emph{scala.AnyRef:} \mintinline[fontsize=\scriptsize]{scala}{String, List, Null, Nothing}\\
\end{breakbox}
\begin{breakbox}
\textbf{List and Tupel:}\\
\emph{List:} Kein Interface, konkrete Klasse, immutable, var. Anz. desselben Typs\\
\begin{scalacode}
val aList = List(2,3,4) // aList: List[Int]
val list = 1 :: 2 :: 3 :: Nil
list.head //1
list.tail //List(2,3)
list(0) //1
\end{scalacode}
\emph{Tupel:} Fixe Anz. Elemente unterschiedlichen Typs\\
\begin{scalacode}
val tupel2 = Tuple2(1, "String") //tupel2: (Int, String)
val theInt = tupel2._1
val triple = (1,2,"3") //triple: (Int, Int, String)
\end{scalacode}
\end{breakbox}
\begin{breakbox}
\textbf{Variablen und Kontrollstrukturen:}\\
\begin{scalacode}
val i = 42 //konstante
var j = 43 //variable
lazy val k = 44 //init bei erstem Zugriff
val max = if(a>b) a else b //ifelse als expression
\end{scalacode}
\end{breakbox}
\begin{breakbox}
\textbf{Pattern Matching:}\\
\begin{scalacode}
//example with Int
def patternMatching(i:Int) = {
i match {
case 0 => "Null"
case 1 => "Eins"
case _ => "?"
}
}
//example with case class
case class Person(name:String, alter:Int)
def matchAPerson(person: Person) = {
person match {
case Person("Peter", 42) => "Found Peter!"
case Person(name, age) if age < 18 => s"minor \$name"
case p if p.alter == 42 => "Person with a magical age!"
case Person(name, _) => s"adult \$name"
//case _ => "unknown person"
}
}
val ruedi = Person("Ruedi", 42)
matchAPerson(ruedi) //person with magical age!
\end{scalacode}
\end{breakbox}
\begin{breakbox}
\textbf{Methoden:}\\
\begin{scalacode}
def add1(m:Int, n:Int):Int = {m+n}
def add2(m:Int, n:Int = 1) = m+n
def add(m:Int)(n:Int) = m+n
add1(10,5)
add2(1)
add1(n=10, m=5)
add(10)(5)
val addToTen = add(10) _ //Partial application e.g. currying
\end{scalacode}
\emph{Call-by-value / Call-by-name}\\
\begin{scalacode}
//call by value
def unless(condition:Boolean, then:Unit) = {
if(!condition) { then }
}
// exception thrown cause all params are
// evaluated from left to right
unless(1+2==3, throw new RuntimeException())
//call by name
def unless(condition:Boolean, then:=>Unit) = {/*...*/}
// then param only evaluated if used
// then parameter is a function with no param
// and Unit as return type
\end{scalacode}
\end{breakbox}
\begin{breakbox}
\textbf{Functions:}\\
\begin{scalacode}
(m:Int, n:Int) => m+n
(m:Int, n:Int) => {m+n}
(m:Int) => (n:Int) => m+n //currying
// 3 main styles
//func1: Int => Int = <function1>
val func1: (Int) => Int = (x:Int) => x^2
//func2: Int => Int = <function1>
val func2 : Function1[Int, Int] = (x:Int) => x^2
//func3: Function[Int,Int] = <function1>
val func3 = new Function[Int, Int] { def apply(x:Int):Int = x^2 }
//inplace
case class Person(name:String, age:Int)
val people = Person("Josef", 21) :: Person("Maria", 17) :: Nil
val isAdult = (p:Person) => p.age >= 18 //predicate
// def filter(p: (A) => Boolean): List[A]
var adults = people.filter(isAdult)
adults = people.filter((p:Person) => p.age >= 18)
adults = people.filter(p => p.age >= 18)
//block allows multiple statements
adults = people.filter{p => p.age >= 18 && p.age < 99}
//function companion object
val isAdult2 = new Function[Person, Boolean] {
def apply(p:Person) : Boolean = p.age >= 18
}
\end{scalacode}
\end{breakbox}
\begin{breakbox}
\textbf{Traits:}\\
\begin{scalacode}
trait Equal[T] {
def isEqual(t:T):Boolean
def isNotEqual(t:T) = !isEqual(t)
}
trait Named { def named:String }
//this mixin with uap is really smooth shit!
case class User(named:String) extends Equal[User] with Named {
def isEqual(t:User):Boolean = this.named == t.named
}
\end{scalacode}
\end{breakbox}
\begin{breakbox}
\textbf{Klassen:}
\begin{itemize}
\item abstrakte und finale Klassen
\item Einfachvererbung
\item Jeder Klasse hat primaeren Ctor, der immer aufgerufen wird\\
\end{itemize}
\emph{Getter and Setter:}
\begin{itemize}
\item keine public Datenfelder
\item Zugriff ueber getter (\mintinline[fontsize=\scriptsize]{scala}{p.age}) und setter (\mintinline[fontsize=\scriptsize]{scala}{p.age = 42})
\item \underline{Uniform Access Principle:} Aufrufer weiss nicht ob property oder methode
\end{itemize}
\begin{scalacode}
abstract class Person(name:String, age:Int) {
def jobDescription : String //abstract
override def toString() = s"name: \$name, age: \$age"
}
class Student(val name:String, age:Int, private val sid:Int)
extends Person(name, age) {
def this(name:String, age:Int, sid:Int, something:String)
= this(name, age, sid) //sec ctor
override def jobDescription = "Student"
val readOnlyVal = 42
var readWriteVar = 43
private val privateVal = 44
def aMethod(f:Int=>Int):Int = {f(42)}
}
val student = new Student("Pamela", 19, 696969)
student.readWriteVar= 44
student.aMethod(x=>x*x)
\end{scalacode}
\end{breakbox}
\begin{breakbox}
\textbf{Case Classes:}\\
\mintinline[fontsize=\footnotesize]{scala}{case class Person(alter:int, name:String)}
\begin{itemize}
\item Getter fuer alle Ctor-Param
\item \mintinline[fontsize=\scriptsize]{scala}{toString, equals, hashCode} automatisch
\item Copy-Methode \mintinline[fontsize=\scriptsize]{scala}{val p = person.copy(age=person.age+5)}
\item Verwendbar im Pattern-Matching
\item kein new noetig \mintinline[fontsize=\scriptsize]{scala}{val p = Person("X",12)}
\end{itemize}
\end{breakbox}
\begin{breakbox}
\textbf{Singleton Objects:}\\
\begin{itemize}
\item Scala hat kein stat. Methoden
\item SO werden autom. instanziert (kein ctor)
\item Heisst das Object gleich wie die Klasse spricht man vom \underline{Companion Object}
\item Bei Case-Classen generiert Comp Companion Object mit apply-Methode (\underline{Factory!})
\end{itemize}
\begin{scalacode}
object Person { //ohne ctor
def apply(name:String) = new Person(name)
}
Person("Mirko") // entspricht Person.apply("")
\end{scalacode}
\end{breakbox}
\begin{breakbox}
\textbf{Scala ADT:}\\
\begin{scalacode}
// FP Styled, for OO-Style include methods in class
sealed class Lst[T]()
case class Nil[T]() extends Lst[T]
case class Cons[T](head:T, tail:Lst[T]) extends Lst[T]
object Lst {
def length[T](lst:Lst[T]):Int = {
lst match {
case Nil() => 0
case Cons(_, tail) => 1+length(tail)
}
}
}
val list:Lst[Int] = Cons(1, Cons(2, Cons(3, Nil())))
Lst.length(list)
\end{scalacode}
\end{breakbox}
\begin{breakbox}
\textbf{Generics with type-bounds:}
\begin{itemize}
\item \mintinline[fontsize=\scriptsize]{scala}{<:} extends
\item \mintinline[fontsize=\scriptsize]{scala}{>:} super
\end{itemize}
\begin{scalacode}
case class Node[T <: Comparable[T] ](value:T,
left:Option[Node[T]], right:Option[Node[T]])
// selfmade using
def using[T <: Closeable](resource : => T)(fun: => Unit) = {
try{fun(resource)} finally {resource.close()}
}
\end{scalacode}
\end{breakbox}
\begin{breakbox}
\textbf{Varianz:}\\
\begin{scalacode}
// Covariant: auch Subtypen erlaubt
// (Return values are in a covariant position)
anAnimal = getGiraffe() -- covariante Umwandlung
// Contravariant: auch Supertypen erlaubt
// (Parameter are in contravariant position)
passAnimal(aGiraffe) -- contravariante Umwandlung
--Baskets
class CoVariantBasket[+T](init:T) {
private val current : T = init
def get : T = current
//def set(t:T) = current = t
}
class ContraVariantBasket[-T](var init:T) {
//private var current : T = init //now allowed
//def get : T = current //now allowed
def set(t:T) = init = t
}
val bananaBasket : CoVariantBasket[Banana] =
new CoVariantBasket[Banana](new Banana)
val fruitBasket : CoVariantBasket[Fruit] = bananaBasket
val fruitBasket2 : ContraVariantBasket[Fruit] =
new ContraVariantBasket[Fruit](new Apple)
val bananaBasket2 : ContraVariantBasket[Banana] = fruitBasket2
\end{scalacode}
\end{breakbox}