Базовые концепции Scala

0. ФП и ООП

Каждое значение - это объект.
                        trait RestClient {
                            val serviceHost: String
                        }

                        class RestClientImpl extends RestClient {
                            override val serviceHost: String = "192.168.1.1"
                        }
                    
Каждая функция - это значение.
                        responseResult.map(result => println(result))
                        

                        def getResponse(sortFromString: String => ColumnSorting[sortType])
                    

I. Выражения (expressions)

Выражение - это вычислимое утверждение:
                        25 + 15
                    
Результат вычислений можно вывести при помощи println:
                        println(25)                   // 25
                        println(25 + 15)              // 40
                        println("Hello!")             // Hello!
                        println("Hello," + " world!") // Hello, world!
                    
Объявление значений (values):
                        val x = 1 + 1
                        println(x) // 2

                        x = 3 // не компилируется

                        val y: Int = 41 - 1
                    
Объявление переменных (variables):
                        var x = 1 + 1
                        x = 3 // компилируется
                    

II. Функции (functions) и методы (methods)

Объявление функции:
                        (x: Int) => x + 1 // анонимная функция

                        val add = (x: Int, y: Int) => x + y
                        println(add(1, 2)) // 3

                        val getTheAnswer = () => 42
                        println(getTheAnswer()) // 42
                    
Объявление метода:
                        def doubleSum(x: Int, y: Int): Int = {
                            val z = x + y
                            z * 2
                        }
                        println(doubleSum(1, 2)) // 6
                    
Значения по умолчанию:
                        def sum(x: Int, y: Int = 11): Int = x + y
                        println(sum(4))    // out: 15
                        println(sum(2, 3)) // out: 5
                    
Именованные аргументы:
                        def fourWords(w0: String = "zero",
                                      w1: String = "one",
                                      w2: String = "two",
                                      w3: String = "three"): Unit =
                            println(s"$w0 $w1 $w2 $w3")

                        fourWords(w3 = "3", w1 = "1") // out: zero 1 two 3
                    

III. Условное выражение (if expression)

Синтакисис аналогичен Java/C#/C++:
                        if (a == b) doSomething()

                        if (trueOrFalse) {
                            doSomething()
                        } else if (a != b) {
                            doSomethingElse()
                        } else {
                            doAnotherThing()
                        }
                    
Результат можно сохранить:
                        val minValue = if (a < b) a else b
                    

IV. Циклы (for loops)

Перебор элементов в коллекции:
                        val strings = Seq("A", "B")
                        val numbers = Seq(1, 2, 3)

                        for (str <- strings) print(s"$str; ") // out: A; B;

                        for (num <- numbers) print(s"$num; ") // out: 1; 2; 3;
                    
Для классов коллекций также доступен foreach:
                        val cars = Seq("Mercedes", "BMW", "Cadillac")
                        cars.foreach(println) // out: Mercedes BMW Cadillac

                        // car => println(car)
                    
Использование генераторов в циклах for:
                        for (i <- 1 to 5) print(s"$i; ") // out: 1; 2; 3; 4; 5;

                        for (i <- 1 until 5) print(s"$i; ") // out: 1; 2; 3; 4;
                    
Использование for для Map:
                        val ingredients = Map(
                            "sugar"  -> 30,
                            "salt"   -> 5,
                            "pepper" -> 10
                        )

                        for ((title, amount) <- ingredients)
                            println(s"ing $title: ${amount}g") // out: ing sugar: 30g ing salt: 5g ing pepper: 10g
                    
Использование "гардов" (guards) в for-выражениях (for comprehensions):
                        for { i <- 1 to 5
                              if (i % 2 == 0)
                        } print(s"i; ") // out: 2; 4;

                        for { i <- 1 to 5
                              if (i % 2 == 0)
                              if (i > 2)
                        } print(s"$i; ") // out: 4;
                    
Конструкция for-yield в for-выражениях:
                        // for (enumerators) yield e
                        val strings = List("A", "B")

                        val res =
                        for { i <- 1 to 5
                              if (i % 2) == 0
                              str <- strings
                        } yield s"$str$i; "

                        res.foreach(print) // out: A2; B2; A4; B4;
                    

V. Циклы (while/do-while)

Синтаксис циклов while и do-while:
                        var i = 0
                        while (i < 5) {
                            print(s"$i; ")
                            i += 1
                        } // out: 0; 1; 2; 3; 4;

                        var j = 2
                        do {
                            print(s"$j; ")
                            j -= 1
                        } while (j > 0) // out: 2; 1;
                    
Возвращает тип Unit:
                        val res =
                            while (i < 5) {
                                someFunction(i)
                                i += 1
                            }
                        print(res) // out: ()
                    

VI. Функции и методы - продолжение

Функции высшего порядка:
                        def getNumbers(): Seq[Int] = Seq(11, 22)

                        def oddOrEven(generator: () => Seq[Int], index: Int): String => String = {
                            def sayOdd(str: String) = s"$str is odd"
                            def sayEven(str: String) = s"$str is even"

                            val numbers = generator()
                            val num = numbers(index)

                            if (num % 2 == 0) sayEven else sayOdd
                        }

                        println(oddOrEven(getNumbers, 0)("That number")) // out: That number is odd
                        println(oddOrEven(getNumbers, 1)("And that")) // out: And that is even
                    
Метод может принимать множественные списки параметров - каррирование (currying):
                        def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = {
                            val sum = x + y
                            sum * multiplier
                        }
                        println(addThenMultiply(1, 2)(3)) // 9

                        def updateOrder(newOrder: Order)(implicit ctx: Context): Unit
                    
Call-by-value:
                    def callByValue(i: Int): Unit = {
                        println(s"i => $i")
                        println(s"ii => $i")
                    }
                    var someInt = 2

                    callByValue ({
                        someInt += 1
                        val x = someInt * someInt
                        x
                    }) // out: i => 9 ii => 9
                
Call-by-name:
                    def callByName(i: => Int) = {
                        println(s"i => $i")
                        println(s"ii => $i")
                    }
                    var someInt = 2

                    callByName ({
                        someInt += 1
                        val x = someInt * someInt
                        x
                    }) // out: i => 9 ii => 16
                

VII. Операторы

Типы операторов:
                        +    -     *     /    %                                         // арифметические операторы
                        ==   !=    >     <    >=                                        // операторы сравнения
                        &&   ||    !                                                    // логические операторы
                        &    |     ^     ~    <<    >>    >>>                           // битовые операторы
                        =    +=    -=    *=    /=    %=    <<=    >>=    &=    ^=    |= // операторы присвоения
                        ()   []    ,     ;                                              // прочие
                    
Приоритет операторов (от высшего к низшему):
Инфиксное (infix) определение операторов:
                        val sum = 1 + 2.+(3)
                        print(sum) // out: 6

                        val mult = 3.*(2) * 2
                        print(mult) // out: 12
                    
Создание операторов:
                        case class Vector2D(x: Double, y: Double) {
                            def * (vec2D: Vector2D) = x * vec2D.x + y * vec2D.y // скалярное произведение
                        }
                        val v = Vector2D(1.0, 1.0)
                        val mult = Vector2D(-2.0, 3.0) * v
                        println(mult) // out: 1.0
                    

VIII. Базовые типы

Базовые типы в Scala и тип String:
Явное преобразование типов (toByte, toShort, toInt, toLong, toChar, toFloat, toDouble, toBoolean (у String)):
                        val s1: Short = 127
                        val s2: Short = 255
                        println(s1.toByte) // out: 127
                        println(s2.toByte) // out: -1
                    
Литералы:
                        val hex = 0x7f   // hex integer
                        val l = 1000L    // l: Long
                        val f = 3.14F    // f: Float
                        val ch = 'A'     // ch: Char
                        val str = "true" // str: String
                    

IX. Коллекции (введение)

Кортежи (Tuples):
                    val tup0 = Tuple2(0, "Zero")
                    val tup1 = Tuple3(1.0F, 1, "One")
                    val tup = (2.0F, 2, "Two", tup0, tup1) // tup: Tuple5(...)

                    println(tup._2) // out: 2

                    val (num, str) = tup0
                    println(num) // out: 0
                    println(str) // out: Zero

                    val users = List(("Mike", 19), ("Joe", 21))
                    for ((name, age) <- users) {
                        println(s"$name is $age years old")
                    }

                    users.foreach {
                        case ("Mike", age) => println(s"Mike is $age years old")
                        case _ => println("unknown user")
                    }
                
Создание экземпляров списков (Lists):
                    val listInt = List(1, 2, 3)
                    val listStr: List[String] = List("One", "Two", "Three")
                
Добавление элементов:
                    val listInt2 = 0 +: listInt // элемент в начало (prepend)
                    val listInt3 = listInt :+ 4 // элемент в конец (append)
                    val list4 = listInt2 ++ listInt3 // объединение
                
Доступ к элементам:
                    for (el <- listStr) print(s"$el; ") // out: One; Two; Three;
                    for (i <- 1 to 2) print(s"${listInt(i)}; ") // out: 2; 3;
                    listInt(3) // java.lang.IndexOutOfBoundsException
                
Создание экземпляра ассоциативного массива (Maps):
                    val diameters = Map(
                        "Mercury" -> 4878,
                        ("Mars", 6779),
                        "Earth" -> 12742
                    )
                
Объединение ассоциативных массивов:
                    val moreDiameters = Map("Pluto" -> 2302) ++ diameters
                
Доступ к элементам:
                    for (d <- diameters) print(s"$d; ") // out: (Mercury,4878); (Mars,6779); (Earth,12742);
                        println(diameters("Mars") // out: 6779

                    diameters("Sun") // java.util.NoSuchElementException
                
Общие методы коллекций (map, filter, foreach, reduce etc.):
                val listInt = List(1, 2, 3)
                val listDoubleInt = listInt.map(x => x * 2) // List(2, 4, 6)

                val filteredInt = listDoubleInt.filter(x => x > 5) // List(6)

                val listStr: List[String] = List("One", "Two", "Three")
                listStr.foreach(println) // out: One Two Three

                listStr.reduce((x, y) => s"$x AND $y") // "One AND Two AND Three"
            
Методы явных преобразований (toList, toMap, toSeq, toSet etc.)
                    val diameters = Map(
                        "Mercury" -> 4878,
                        "Mars" -> 6779,
                        "Earth" -> 12742
                    )
                    diameters.toSeq // Seq[(String, Int)]((Mercury,4878), (Mars,6779), (Earth,12742))
                
Изменяемые коллекции (mutable):
                    import scala.collection.mutable.ListBuffer

                    val mutableList = ListBuffer.empty[Int]
                    mutableList += 1
                    mutableList ++= List(2, 3)
                    mutableList.foreach(println) // out: 1 2 3
                
                    import scala.collection.mutable.Map

                    val diameters = Map(
                        "Mars" -> 6779,
                        "Earth" -> 12742
                    )
                    diameters += "Uranus" -> 51118
                    diameters("Mars") = -100
                    diameters ++= Map("A-C" -> diameters("Earth"))
                    diameters -= "Earth"
                    println(diameters) // out: Map(A-C -> 12742, Uranus -> 51118, Mars -> -100)