Scala Nejslavnější operní dům Itálie předminulého století....
Jiří 'Jira' Mareš víc než 10 let s Javou GREEN Center -> BOSCH -> ČSAD SVT Praha zaměřuji se na proces vývoje a na kvalitu kódu jirablog.blogspot.com twitter.com/jiramares
Ondra 'Satai' Nekola sedm let s Javou (v dobrém i zlém) Globe Internet -> Active 24 -> Wirenode -> Sun Microsystems (WTK team) twitter.com/satai satai.posterous.com www.nekola.cz starosta v Lokále
SCALA První kroky
ScaLa Scalable Language
Scala v hyperkostce jazyk pro JVM (a .NET) objektový jazyk funkcionální jazyk akademický projekt prakticky použitelný jazyk snadná spolupráce s Javou (a dalšími JVM jazyky) silný typový systém ...a není potřeba být moc ukecaný síla v knihovnách spíše než v jazyce vysoký výkon
Objektový jazyk (skoro) vše je objekt operace s čísly a booleany většinou kompilované jako instrukce nad primitivnímy typy neexistují statické metody a data jednoduchá dědičnost + traity
Tři módy interaktivní mód REPL (Read-Eval-Preview Loop)
DEMO
Tři módy interaktivní mód REPL (Read-Eval-Preview Loop) script parametry v poli args (Array [String]) třídy v souborech i více public tříd v jednom souboru dobře škáluje a spolupracuje s antem, mavenem, IDEs...
Funkcionální jazyk funkce je objekt (first class value) můžeme předávat a ukládat do proměnné funkci mohu definovat uvnitř jiné funkce mohu pro definici použít funkční literál (funkce beze jména, λ) funkce je zobrazení (žádné sideefecty) ruku v ruce jdou immutable typy lepší škálovatelnost, protože nemusím synchronizovat cachování výpočetně náročných volání
A jdeme na to... ...ale raději pomalu
Val scala> val x : Int = 1 x: Int = 1 scala> val y = 2 y: Int = 2 scala> val z = x + y z: Int = 3 scala> z = 3
:7: error: reassignment to val z=3 ^
Var scala> val x1 : Int = 1 x1: Int = 1 scala> val y1 = 2 y1: Int = 2 scala> var z1 = x1 + y1 z1: Int = 3 scala> z1 = 4 z1: Int = 4 scala> z1 res0: Int = 4
A nejlínější konstanta na světě scala> var x2 : Int = 1 x2: Int = 1 scala> val y2 = 2 y2: Int = 2 scala> lazy val z2 = x2 + y2 z2: Int = scala> x2 = 3 x2: Int = 3 scala> z2 res1: Int = 5
Funkce (živočicháře) scala> def sum(a:Int, b:Int) : Int = a + b sum: (a: Int,b: Int)Int scala> def multi(a:Int, b:Int) = a * b multi: (a: Int,b: Int)Int scala> def succ(a: Int) : Int = { | return sum(a, 1) |} succ: (a: Int)Int scala> sum(1, 2) res4: Int = 3
if/else je výraz def plural(noun: String) = if ("man" == noun) "men" else noun + "s"
if není tak úplně výraz (vlastně vůbec ne) scala> val a = 4 a: Int = 4 scala> if (4 == a) println("Je to ctyri") Je to ctyri scala> if (5 == a) println("Je to pet") <>
Faktoriál - povinná složka každé učebnice scala> def factorial(n : Int): Int = | if (0 == n) 1 else n * factorial(n-1) factorial: (n: Int)Int scala> factorial(6) res12: Int = 720
case class scala> case class Pivo(jmeno : String, plnotucne : Boolean) defined class Pivo scala> val bernard = Pivo("Bernard", true) bernard: Pivo = Pivo(Bernard,true) scala> bernard.jmeno res1: String = Bernard scala> bernard.toString res2: String = Pivo(Bernard,true)
case class
case class Pivo(jmeno : String, plnotucne : Boolean){ def vypijS(kolega : String) = println("Gloglo " + jmeno + " s " + kolega) }
scala> val bernard = Pivo("Bernard", true) bernard: Pivo = Pivo(Bernard,true)
scala> bernard.vypijS("Petr") Gloglo Bernard s Petr
scala> bernard vypijS "Pavel" Gloglo Bernard s Pavel
I operátor je jen metoda scala> val i = 1 i: Int = 1 scala> i + 1 res15: Int = 2 scala> i.+(1) res14: Int = 2
Typová inference - kam orli nelétají scala> def factorial(n : Int) = | if (0 == n) 1 else factorial(n-1) :5: error: recursive method factorial needs result type if (0 == n) 1 else factorial(n-1) ^
Funkce je taky... objekt
scala> (x: Int, y: Int) => x + y
res14: (Int, Int) => Int =
scala> val mojeFunkce = (x: Int, y: Int) => x + y
mojeFunkce: (Int, Int) => Int =
scala> mojeFunkce(1,2)
res15: Int = 3
scala> val taSamaFunkce = mojeFunkce
taSamaFunkce: (Int, Int) => Int =
Seznam: najdu tam, co... scala> val inty = 1 :: 2 :: 3 :: 4 :: Nil inty: List[Int] = List(1, 2, 3, 4) scala> inty == List(1, 2, 3, 4) res17: Boolean = true scala> 1 :: 2 :: List(3, 4) res18: List[Int] = List(1, 2, 3, 4) scala> List(1, 2) ::: List(3, 4) res19: List[Int] = List(1, 2, 3, 4)
A znáte ten scalovej fór? scala> val inty = 1 :: 2 :: 3 :: 4 :: Nil inty: List[Int] = List(1, 2, 3, 4) scala> for (i <- inty) | println(i) 1 2 3 4 scala> for (i <- inty if i > 2) println (i) 3 4
A znáte ten scalovej fór? val inty = 1 to 10 inty: Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) for (i <- inty) yield i res0: RandomAccessSeq.Projection[Int] = RangeM(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val oddies = for (i <- inty if (i % 2 == 1 )) yield i oddies: Seq.Projection[Int] = RangeFM(1, 3, 5, 7, 9)
Jde to i knihovnami... scala> val inty = 1 to 10 inty: Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> inty.filter( (x : Int) => x > 6) res2: Seq.Projection[Int] = RangeF(7, 8, 9, 10) scala> inty.filter( _ > 6) res5: Seq.Projection[Int] = RangeF(7, 8, 9, 10)
Jde to i knihovnami... scala> val inty = 1 to 10 inty: Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> def isOdd(n : Int) = 1 == (n % 2) isOdd: (Int)Boolean scala> inty.filter( x => isOdd(x) ) res7: Seq.Projection[Int] = RangeF(1, 3, 5, 7, 9) scala> inty.filter( isOdd(_) ) res6: Seq.Projection[Int] = RangeF(1, 3, 5, 7, 9) scala> inty.filter( isOdd ) res3: Seq.Projection[Int] = RangeF(1, 3, 5, 7, 9) scala> inty filter isOdd res3: Seq.Projection[Int] = RangeF(1, 3, 5, 7, 9)
Nejen filtr... scala> val inty = 1 to 10 inty: Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> inty.map( _ * 10) res8: RandomAccessSeq.Projection[Int] = RangeM(10, 20, 30, 40, 50, 60, 70, 80, 90, 100) scala> inty.reduceLeft( _ + _) res9: Int = 55 scala> inty.foldLeft(100)(_ + _) res11: Int = 155
Ekvivalenty k primitivním typům předek všech je AnyVal (obvykle) se generuje slušný bytecode Byte, Short, Int, Long, Char, String, Float, Double, Boolean literály jsou stejné jako v Javě Unit místo void
Multiline String val s = """Dei Kobol una apita uthoukarana Ukthea mavatha gaman kerimuta Obe satharane mua osavathamanabanta Api obata yagnya karama""" s: java.lang.String = Dei Kobol una apita uthoukarana Ukthea mavatha gaman kerimuta Obe satharane mua osavathamanabanta Api obata yagnya karama všimněte si - java.lang.String
"Operátory" scala> val n = 42 n: Int = 42 scala> n + 3 res17: Int = 45 scala> n.+(3) res18: Int = 45 scala> -n res19: Int = -42 scala> n.unary_res23: Int = -42
Rovnosti a nerovnosti scala> val x = 1 :: 2 :: 3 :: Nil x: List[Int] = List(1, 2, 3) scala> val y = 1 :: 2 :: 3 :: Nil y: List[Int] = List(1, 2, 3) scala> x == y res24: Boolean = true scala> x eq y res25: Boolean = false scala> x != y res26: Boolean = false scala> x ne y res27: Boolean = true
Acociativita a priorita operátory končící dvojtečkou jsou asociativní zprava, jinak zleva scala> 1 :: 2 :: Nil res0: List[Int] = List(1, 2) scala> Nil.::(2).::(1) res1: List[Int] = List(1, 2) jinak určuje první znak nfixové operátory |, ^, &, < >, =, !, :, + -, * / %, ~ prefixové operátory +, -, !, ~
case class - opakování case class Pivo(jmeno : String, plnotucne : Boolean) { def vypijS(kolega : String) = println("Gloglo " + jmeno + " s " + kolega) }
scala> val bernard = Pivo("Bernard", true) bernard: Pivo = Pivo(Bernard,true) scala> bernard.vypijS("Petr") Gloglo Bernard s Petr scala> bernard vypijS "Pavel" Gloglo Bernard s Pavel
Classa běžná, commonová class Osoba(pJmeno:String, pPrijmeni:String) { val jmeno = pJmeno val prijmeni = pPrijmeni def vypis() = println("Osoba: " + jmeno + " " + prijmeni) } scala> val plukovnik = new Osoba("Saul", "Tight") plukovnik: Osoba = Osoba@a87cbde scala> plukovnik.vypis() Osoba: Saul Tight scala> plukovnik.jmeno res31: String = Saul
Dědičnost class Cylon(jmeno: String, prijmeni: String, pModel: Int) extends Osoba(jmeno, prijmeni) { val model = pModel override def vypis() = println("Cylon: " + jmeno + " " + prijmeni + " ma plan") } scala> val sharon = new Cylon("Sharon", "Valeri", 8) sharon: Cylon = Cylon@791d5d85 scala> sharon.jmeno res32: String = Sharon scala> sharon.vypis Cylon: Sharon Valeri ma plan
Jak zbytečně nepsal kopírování položek class Zvire(val jmeno: String, val druh: String){ def vypis() = println("zvire: " + jmeno) } scala> val amina = new zvire("Amina", "zirafa") amina: Zvire = Zvire@42c9aff0 scala> amina.vypis zvire: Amina scala> amina.druh res35: String = zirafa
Konstruktor to, co je v těle třídy funguje jako konstruktor pokud chceme více přetížených konstruktorů, tak stačí definovat metodu jménem this pomocný konstruktor musí volat jako první akci this() volání konstruktoru předka class A(f: Int, g: String) extends B(f) { }
Obzvláště dementní příklad (by OSN) class Foo(label: String) { require(label != null) println(label); def this(label:String, i: Int) = { this(label + " " + i) } } scala> new Foo("ahoj") ahoj res36: Foo = Foo@455d54a7 scala> new Foo("ahoj", 3) ahoj 3 res37: Foo = Foo@25719b33
Bude se vám hodit u zkoušky jedno jsou metoda bez parametru def size = 4 atribut val size = 4 atributy a metody jsou v jednom namespace je možné metodu overridovat pomocí val scala> class class A { def size = 4 } defined class A scala> class B extends A { override val size = 3 } defined class B
Singleton (Dagi promine) object SoLonely { val stav = "Lonely" } scala> SoLonely.stav res38: java.lang.String = Lonely Samozřejmě není tak úplně jedináček!
Třída a její kumpán class WorldlyGreeter(greeting: String) { def greet() = { val worldlyGreeting = WorldlyGreeter.worldify(greeting) println(worldlyGreeting) } } object WorldlyGreeter { def apply(s: String) = new WorldlyGreeter(s) def worldify(s: String) = s + ", world!" } val greeter = WorldlyGreeter("Hallo")
Řízení běhu if/else expression if neexpresion (Unit) while a do-while neexpressions for expression try/catch/finally expression match expression - pattern matching nejedná se o REGEXPy! neexistuje break a continue, lze je odsimulovat nejstylovějším způsobem řízení je rekurze
Funkce v Lokále scala> def vnejsi(): Unit = { | def vnitrni() : Int = 42 | println(vnitrni()) |} vnejsi: ()Unit scala> vnejsi() 42
Funkce je taky... objekt
scala> (x: Int, y: Int) => x + y
res14: (Int, Int) => Int =
scala> val mojeFunkce = (x: Int, y: Int) => x + y
mojeFunkce: (Int, Int) => Int =
scala> mojeFunkce(1,2)
res15: Int = 3
scala> val taSamaFunkce = mojeFunkce
taSamaFunkce: (Int, Int) => Int =
Closures = funkce s volňáskem scala> var another = 10 another: Int = 10 scala> val count = (x: Int) => x + another count: (Int) => Int = scala> count(10) res0: Int = 20 scala> another = 15 another: Int = 15 scala> count(10) res1: Int = 25
Částečně aplikované funkce na curry scala> def sum(x: Int, y: Int) = x + y sum: (x: Int,y: Int)Int scala> def succ(x : Int) = sum(x, 1) succ: (x: Int)Int scala> succ(1) res16: Int = 2 scala> val succ2 = (x : Int) => sum(x, 1) succ2: (Int) => Int = scala> succ2(3) res16: Int = 4
Lazy parametry - podle jména scala> def log(toLog: Boolean, msg: => String) = | if (toLog) println("log: " + msg) log: (toLog: Boolean,msg: => String)Unit scala> def message(s1: String, s2: String) = { | println("Jsme tu :-)") | s1 + s2 |} message: (s1: String,s2: String)java.lang.String scala> log(true, message("A", "B")) Jsme tu :-) log: AB scala> log(false, message("A", "B"))
Trait - mocné rozhraní trait je interface, ale může obsahovat: definici metody atributy trait se liší od třídy nemůže mít parametry - nejde trait A(i: Int) {} super je v třídách staticky vyhodnocované v traitech dynamicky trait se mixuje do (mix-in) třídy pomocí extends extends nebo with při mixování více traitů záleží na jejich pořadí traity jsou lepší než interfacy a než vícenásobná dědičnost
Trait na živo scala> trait trait T { def t = 3 } defined trait T scala> class A defined class A scala> def a = new A with T a: A with T scala> a.t res12: Int = 3 scala> class B extends T defined class B scala> class C extends A with T
Pattern matching potřebujeme case classes jde to i bez nich case class znamá, že compilátor: vytvoří companion object s factory metodou všechny parametry třídy dostanou prefix val naimplementuje metody toString, equals, hashcode
Picí test class Piti case class Nealko(nazev: String) extends Piti case class Alko(obsahAlkoholu: Int, nazev: String) extends Piti def vypij(piti: Piti) = piti match match { case Nealko(nazev) => println("Muzes kolik chces " + nazev) case Alko(procenta, nazev) => if (procenta < 10) println ("Muzes i " + nazev) else println("Nemuzes " + nazev + " protoze obsahuje " + procenta + " alkoholu") } scala> vypij(new Nealko("Cola")) Muzes kolik chces Cola scala> vypij(new Alko(8, "Pivo")) Muzes i Pivo scala> vypij(new Alko(30, "Vodka"))
Neměnitelné listí List - podstatné metody jsou head, tail a isEmpty isEmpty podporuje pattern matching scala> val List(a, b, c) = 1 :: 2 :: 3 :: Nil a: Int = 1 b: Int = 2 c: Int = 3 přístup k prvkům pomocí kulatých závorek scala> val val l = 1 :: 2 :: 3 :: Nil l: List[Int] = List(1, 2, 3) scala> l(1) res18: Int = 2 scala> l.apply(1)
Mapy a tupláky scala> val map = Map(1 -> "1", 2 -> "2") map: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,1), (2,2)) scala> map(1) res19: java.lang.String = 1 scala> def viceNavratovychHodnot = (1,2.0,"Pozdrav") viceNavratovychHodnot: (Int, Double, java.lang.String) scala> val val (i, d, s) = viceNavratovychHodnot i: Int = 1 d: Double = 2.0 s: java.lang.String = Pozdrav
Jak udělat v kódu pořádný zmatek scala> implicit implicit def int2String(x: Int) = x.toString int2String: (x: Int)java.lang.String scala> def a(x: String) = println("Mam " + x) a: (x: String)Unit scala> a(1) Mam 1