├── README.md ├── chapters ├── Asynchronicity.md ├── Basic-control-structures.md ├── Basic-functional-constructs.md ├── Classes-and-objects.md ├── Collections-library.md ├── Dissection-of-Hello-World.md ├── Generics-and-type-members.md ├── Handling-data-in-functional-way.md ├── Implicits.md ├── Macros.md ├── Methods-and-operators.md ├── Modularization-and-object-orientation.md ├── Notes.md ├── Packages-and-imports.md ├── Traits.md ├── Type-system-basics.md └── Variance.md └── images └── scalatypes.png /README.md: -------------------------------------------------------------------------------- 1 | # Somewhat opinionated Scala guide 2 | 3 | This is a tutorial intended to guide developers already familiar with Java through the Scala language. It mostly focuses on language itself and the way it changes basic programming style as compared to Java. It is also somewhat opinionated in terms of usability of various Scala features and stylistic recommendations. 4 | 5 | 1. [Dissection of Hello World](chapters/Dissection-of-Hello-World.md) 6 | 2. [Basic control structures](chapters/Basic-control-structures.md) 7 | 3. [Type system basics](chapters/Type-system-basics.md) 8 | 4. [Methods and operators](chapters/Methods-and-operators.md) 9 | 5. [Basic functional constructs](chapters/Basic-functional-constructs.md) 10 | 6. [Handling data in functional way](chapters/Handling-data-in-functional-way.md) 11 | 7. [Packages and imports](chapters/Packages-and-imports.md) 12 | 8. [Classes and objects](chapters/Classes-and-objects.md) 13 | 9. [Traits](chapters/Traits.md) 14 | 10. [Generics and type members](chapters/Generics-and-type-members.md) 15 | 11. [Variance](chapters/Variance.md) 16 | 12. [Implicits](chapters/Implicits.md) 17 | 13. [Collections library](chapters/Collections-library.md) [draft] 18 | 14. [Macros](chapters/Macros.md) [draft] 19 | -------------------------------------------------------------------------------- /chapters/Asynchronicity.md: -------------------------------------------------------------------------------- 1 | # Asynchronicity 2 | 3 | ## Synchronous vs asynchronous execution 4 | 5 | * Having a program being a sequence of operations, they may be executed synchronously or asynchronously 6 | * Asynchronous execution lets us do other things while waiting until an operation finishes 7 | * Therefore, asynchronous execution introduces *concurrency* 8 | * This concurrency may be implemented either as parallelism or as non-blocking waiting 9 | * Old style Java `Future` example - run something, do other things, then `get` 10 | * This way we can batch and "parallelize" non-blocking waiting 11 | * This asynchronicity is not usually what we want 12 | 13 | ## Non-blocking operations 14 | 15 | * Some operations in our program may wait for long or possibly indefinite amount of time 16 | * This program will be run multiple times, concurrently. The longer it is, the more executions running simultaneously 17 | at given time. 18 | * If the waiting operation is blocking the thread, we may need a lot of threads. This also means a lot of context switches 19 | which may degrade performance. 20 | * We want the long-waiting operation to be non-blocking on the thread. 21 | * Currently, in Java and Scala, the only way to achieve this is by making it *asynchronous*. There are languages 22 | which can express non-blocking programs (with underlying asynchronous execution) retaining the desired synchronous 23 | syntax: Erlang, Haskell. 24 | * We don't care that much about "forking" capabilities of asynchronicity, we want non-blocking execution and an ability 25 | to specify *continuations*. Therefore, we may write our program in *continuation passing style*. 26 | * There's another name for continuation passing style: *callback hell*. 27 | 28 | ## `Async` abstractions 29 | 30 | * We can transform and compose them 31 | * Errors propagate themselves naturally 32 | * Ultimately, we can get the best syntax in Scala with for comprehensions 33 | * The callback nature of our abstraction only manifests itself at the boundaries 34 | * We still have some problems: 35 | * exception handling 36 | * where to invoke callbacks? 37 | 38 | ## `Future` 39 | 40 | * "A value detached from time" 41 | * Creating `Future`s: `Future.unit`, `Future.successful`, `Future.eval`, `Future.apply` 42 | * `Promise`s 43 | * `Future.sequence`/`traverse` 44 | * managing parallelism 45 | * 46 | * `ExecutionContext` 47 | * `execute` + `reportFailure` 48 | * thread pool wrapping, `ExecutionContextExecutor` 49 | * execution may involve acquiring locks, managing some thread locals, etc. 50 | * `RunNowEC` + `*Now` extension methods 51 | * `RunInQueueEC` 52 | * `global` 53 | * Problems: 54 | * pointless context switching -> performance degradation 55 | * we must drag the execution context with us all the time 56 | * choosing the right execution context is not easy 57 | * blocking operations must be handled extremely carefully 58 | * Alternatives: Monix `Task`, cats/scalaz `IO` 59 | * Monix `Task` 60 | * represents a program 61 | * retains referential transparency 62 | * explicit async boundaries 63 | -------------------------------------------------------------------------------- /chapters/Basic-control-structures.md: -------------------------------------------------------------------------------- 1 | # Basic control structures 2 | 3 | ## Local variables 4 | 5 | Scala has two keywords to define local variables: `val` and `var`. The `val` keyword defines a *value* - a "variable" that cannot change its value after initialization. The `var` keyword denotes a mutable variable. 6 | 7 | ```scala 8 | val name: String = "Fred" 9 | var counter: Int = 0 10 | ``` 11 | 12 | As you can see, the type of variable is declared after its name and separated from it with a colon `:`, similarly to how method return type is declared. This syntax of "typing" things with a colon is used in many more contexts and is generally called *type ascription*. 13 | 14 | After the type comes the `=` sign and an initial value. The value can be arbitrary expression. From the previous section about Hello World we know that in Scala, even a block is an expression and can be assigned to a variable. This comes in handy when we need to perform some more complex computations to obtain the value, e.g. 15 | 16 | ```scala 17 | //TODO better example 18 | val adjustedString: String = { 19 | val str = fetchSomeString() 20 | str.substring(0, str.length-1).toUpperCase 21 | } 22 | ``` 23 | 24 | ### Type inference 25 | 26 | Type inference also works for local variables. We could omit type declarations and write our original example as: 27 | 28 | ```scala 29 | val name = "Fred" 30 | var counter = 0 31 | ``` 32 | 33 | However, it is recommended that for mutable variables (`var`s), the type should always be explicit. This is because the value of a `var` may change and therefore should not be inferred just from the initial value. The type of initial value may be narrower than what we intended. So, the recommended version of our example would be: 34 | 35 | ```scala 36 | val name = "Fred" 37 | var counter: Int = 0 38 | ``` 39 | 40 | ### Mutable variables 41 | 42 | Scala encourages programming style that leverages immutability. Therefore, mutable variables should be avoided as much as possible. As you progress in your learning of Scala, you will see that it has many interesting features which make it possible. Many situations which require mutable variables in Java can be completely avoided in Scala. Try to express as much as you can with `val`s. 43 | 44 | ## `if` expression 45 | 46 | Scala's `if` is similar to Java: 47 | 48 | ```scala 49 | val x = 5 50 | if(x < 10) { 51 | println("<10") 52 | } else if (x == 10) { 53 | println("=10") 54 | } else { 55 | println(">10") 56 | } 57 | ``` 58 | 59 | However, there is similar difference between `if`s in Java and `if`s in Scala as there is for blocks. In Java, `if-else` is purely imperative structure, whereas in Scala it is a valid expression that can be assigned to variables or passed as arguments. Therefore, the above example can be refactored to: 60 | 61 | ```scala 62 | val x = 5 63 | println( 64 | if(x < 10) { 65 | "<10" 66 | } else if(x == 10) { 67 | "=10" 68 | } else { 69 | ">10" 70 | } 71 | ) 72 | ``` 73 | 74 | This can be further simplified - we don't need to wrap bodies of `if` and `else` into a block. They can also be arbitrary expressions: 75 | 76 | ```scala 77 | println(if(x < 10) "<10" else if(x == 10) "=10" else ">10") 78 | ``` 79 | 80 | Scala's `if` is *always* an expression - even if the `else` clause is missing. If so, then what will the following code print? 81 | 82 | ```scala 83 | val x = 15 84 | println(if(x < 10) "<10") 85 | ``` 86 | 87 | It turns out that this code prints `()` (the "unit"). This is because Scala always implicitly adds a missing `else` clause and fills it with the `()`. The example above is actually translated to: 88 | 89 | ```scala 90 | val x = 15 91 | println(if(x<10) "<10" else ()) 92 | ``` 93 | 94 | This can be *very* tricky, especially if you forget the `else` clause when assigning to a variable: 95 | 96 | ```scala 97 | val x = 15 98 | val str = if(x < 10) "<10" 99 | ``` 100 | 101 | Scala compiler will *not* issue an error here. Instead, it will add the implicit `else ()` clause and infer the type of `str` as `Any`, which is the most common type of `Int` from the `if` clause and `Unit` from the `else` clause. This can later cause compilation errors which are very hard to understand if you are not familiar with this peculiar Scala rule. Rule of thumb is that if you're getting strange compilation errors involving type `Any`, see if you haven't forgotten an `else` clause somewhere. 102 | 103 | It is also worth to note that since Scala's `if` is an expression, there is no longer a need for the ternary conditional operator known from C and Java (`?:`). Scala doesn't have it. 104 | 105 | ## Equality comparisons 106 | 107 | In Java, the `==` and `!=` operators perform value comparison on primitive types, but they do a reference comparison for objects. If you want to perform proper equality comparison on objects, you need to use the `equals` method. 108 | 109 | In Scala, this is different. The `==` and `!=` operators *always* perform value equality. For objects, they internally call the `equals` method. They also handle situations where any of the operands is `null`, so you don't have to put any nullguards. For example, you can now safely write: 110 | 111 | ```scala 112 | val str: String = fetchSomeStringThatMayBeNull() 113 | if(str == "something") { ... } 114 | ``` 115 | 116 | If you really need to use reference comparison in Scala, you can still do it with the `eq` and `ne` operators. 117 | 118 | ## Lazy vals 119 | 120 | Scala has one more flavor of local variables, the `lazy val`. It is equivalent to `val` except that its value is computed lazily, upon first reference to the `lazy val`. Lazy local values can help us make our code cleaner by making small refactorings easier. Consider the following example: 121 | 122 | ```scala 123 | def processArgs(args: Array[String], start: String, end: String) = { 124 | // args(0) refers to the first element of args 125 | if(args.nonEmpty && args(0).toLowerCase.startsWith(start) && args(0).toLowerCase.endsWith(end)) { 126 | // do something 127 | } 128 | } 129 | ``` 130 | 131 | The code above is a bit ugly. The `if` condition has duplicated code - we refer to `args(0).toLowerCase` twice. This is bad for two reasons: 132 | * it violates the DRY principle and makes code verbose 133 | * it may be a performance problem - we evaluate the same expression twice 134 | 135 | The natural solution would be to extract the duplicated expression to a variable, like so: 136 | 137 | ```scala 138 | val arg = args(0).toLowerCase 139 | if(args.nonEmpty && arg.startsWith(start) && arg.endsWith(end)) { 140 | // do something 141 | } 142 | ``` 143 | 144 | But we have a problem - if `args` is empty, this will fail with an `ArrayIndexOutOfBoundsException`, because `arg` is assigned before we check for non-emptiness. 145 | 146 | In order to fix this, we can simply turn the `val` into a `lazy val`: 147 | 148 | ```scala 149 | lazy val arg = args(0).toLowerCase 150 | if(args.nonEmpty && arg.startsWith(start) && arg.endsWith(end)) { 151 | // do something 152 | } 153 | ``` 154 | 155 | This way `args(0).toLowerCase` will not be evaluated until the non-empty check is positive. It is also guaranteed that it will be evaluated at most once. 156 | 157 | ## Local methods 158 | 159 | In Scala it is possible to define methods locally - any block of code can contain method definitions. They are visible only inside that block. Let's take the example from `lazy val` description and modify it as follows: 160 | 161 | ```scala 162 | def processArguments(args: Array[String], start: String, end: String) = { 163 | def checkArg(arg: String) = 164 | arg.startsWith(start) && arg.endsWith(end) 165 | if(args.nonEmpty && checkArg(args(0).toLowerCase)) { 166 | // do something 167 | } 168 | } 169 | ``` 170 | 171 | Local methods are better than plain private methods for following reasons: 172 | * Local method can refer to all the values visible at the point where it's defined. For example, our `checkArg` method can access the `start` and `end` parameters of its outer method. If it were a plain private method, it would have to accept them as its own parameters. 173 | * Local method is visible only where it's actually needed. We do not pollute other namespaces. This also makes it easier to read the code. 174 | 175 | Local methods can greatly improve readability. They allow you to give local but meaningful names to small pieces of your code while still keeping it concise. 176 | 177 | ## Loops 178 | 179 | Scala has `while` and `do-while` loops: 180 | 181 | ```scala 182 | var i = 0 183 | 184 | while(i < 100) { 185 | println(i) 186 | i += 1 187 | } 188 | 189 | do { 190 | println(i) 191 | i -= 1 192 | } while(i >= 0) 193 | ``` 194 | 195 | Again, the difference from Java is that both `while` and `do-while` are expressions. They are not very interesting though, because they always evaluate to `()`. These loops are very rarely used in Scala, even than in Java - usually only in some low-level or performance critical code. 196 | 197 | In the above example, you may have also noticed that we incremented and decremented our local variable using `+=` and `-=`. Scala does not have the C-style prefix and postfix operators `++` and `--`. Scala also doesn't have the `break` and `continue` keywords. 198 | 199 | Technically, Scala doesn't have a `for` loop. Instead, it has a more general construct called _for comprehension_, which can be used as a foreach-style loop. We will not cover the entire _for comprehension_ syntax here, but only show how to use it like it's a loop. Example: 200 | 201 | ```scala 202 | val args: Array[String] = fetchArgs() 203 | for(arg <- args) { 204 | println(arg) 205 | } 206 | // (x to y) creates an object which represents an integer range from x to y, inclusively 207 | for(i <- (0 to args.length-1)) { 208 | println(args(i)) 209 | } 210 | ``` 211 | 212 | The `for` "loop" above is actually just a syntactic sugar for calling the `foreach` method which takes some action and invokes it for every element. The above example is equivalent to: 213 | 214 | ```scala 215 | val args: Array[String] = fetchArgs() 216 | args.foreach(arg => println(arg)) 217 | (0 to args.length-1).foreach(i => println(args(i))) 218 | ``` 219 | 220 | The first loop can be even shorter: `args.foreach(println)`. We are using lambdas and higher-order functions here. We will cover them in more detail later. 221 | Loops written using `for` comprehensions are somewhat less performant than `while` and `do-while` loops due to usage of lambdas, whose body must be compiled to a separate anonymous class. 222 | Loops in Scala can be avoided much more than in Java thanks to various higher-order functions available on collections and usage of tail recursion. We will cover these topics in some other chapter. 223 | 224 | ## The `return` keyword 225 | 226 | Do not use `return` keyword. Although it works like in Java - exits the method and returns a value - it is rarely needed thanks to the fact that most Scala constructs are valid expressions and can be used as method body. The `return` keyword also interacts poorly with type inference, forcing you to always explicitly declare return type of a method. 227 | 228 | ## Switch 229 | 230 | Scala doesn't have the C-style `switch` construct. Instead, it has a much more powerful feature - pattern matching. We will not cover it fully here, but only show how to use it similarly to `switch`: 231 | 232 | ```scala 233 | val x: Int = fetchSomeInt() 234 | x match { 235 | case 0 => println("zero") 236 | case 1 => println("one") 237 | case 2 => println("two") 238 | case _ => println("other") 239 | } 240 | ``` 241 | 242 | Just like with any other construct shown before, the pattern match is an expression, so we could refactor the above into: 243 | 244 | ```scala 245 | val x: Int = fetchSomeInt() 246 | println(x match { 247 | case 0 => "zero" 248 | case 1 => "one" 249 | case 2 => "two" 250 | case _ => "other" 251 | }) 252 | ``` 253 | 254 | Important things to remember about pattern matching used like this: 255 | * Pattern matching works with any type, not just `int`, `short`, `byte`, `char`, `String` and `Enum`s like in Java. 256 | * If the matched value is `null`, it won't cause immediate `NullPointerException` like in Java. Actually, you can match against `null` in one of the cases. 257 | * There is no `break` keyword needed after each case (there's no such keyword in Scala) 258 | * Each case has its own scope, i.e. you can declare local variables after each `=>` sign without enclosing everything in a block. 259 | * If you forget the return value in one of the cases, i.e. write nothing after the `=>` sign, Scala compiler will implicitly put `()` in there. You may run into similar problems as with the implicitly added `else ()` clause. 260 | * If you don't provide the default `case _ => something` and matched value won't fall into any other case, a `MatchError` will be thrown. 261 | 262 | ## Exceptions 263 | 264 | Scala has the same syntax for throwing exceptions as Java - it uses the `throw` keyword. It also has a similar syntax for catching exceptions, with the usual `try`, `catch` and `finally` clauses: 265 | 266 | ```scala 267 | def intOrZero(str: String): Int = 268 | // Scala has some nice API to parse strings into numbers 269 | try str.toInt catch { 270 | case nfe: NumberFormatException => 0 271 | } finally { 272 | doSomeCleanup() 273 | } 274 | ``` 275 | 276 | The above method tries to convert its `String` argument to integer value or returns 0 if the conversion fails. 277 | 278 | The differences from Java are: 279 | * The `try` keyword accepts arbitrary expression. 280 | * There is at most one `catch` block. It uses pattern matching to handle different types of exceptions and can leverage full pattern matching capabilities. As we already noted, this is out of scope of this chapter. Note that the `catch` block also evaluates to some value - `0` in our example. 281 | * The entire `try-catch-finally` construct is - no surprise - an expression. In above example we have used it as a method body. It evaluates to either the value inside `try` or value returned by the `catch` block. If there is a `Throwable` which does not fall into any of the cases inside the `catch` block, it is rethrown. 282 | * There is no "try-with-resources" syntax, but it can be fairly easily simulated using lambdas, by-name arguments and higher order functions - these will be covered later. 283 | 284 | ### Do not catch `Throwable` in Scala 285 | 286 | It is generally considered harmful to catch `Throwable` in Scala. This is because Scala sometimes uses some special types of throwables in regular language features. For example, sometimes the `return` instruction causes a `NonLocalReturnControl` to be thrown. We won't be digging into these details, especially considering the fact that they are mostly used by discouraged language features (like the `return` keyword). At this point let's just assume that catching throwables is bad. 287 | 288 | If you want to catch something more than `Exception` but less than `Throwable`, you may use a special `NonFatal` pattern: 289 | 290 | ```scala 291 | try doSthDangerous() catch { 292 | case NonFatal(t) => t.printStackTrace() 293 | } 294 | ``` 295 | 296 | This will catch all throwables except for the special ones used by Scala and some very severe errors like `OutOfMemoryError`. So it may be a good practice to use `NonFatal` everywhere where you would catch `Throwable` in Java. 297 | 298 | ### Checked exceptions 299 | 300 | Scala does not have them. 301 | 302 | It does not force you to catch any exceptions as well as it does not require you to declare them in your method signatures. However, there is an annotation that simulates the Java's `throws` declaration which can be used for compatibility with Java: 303 | 304 | ```scala 305 | @throws(classOf[IOException]) 306 | def readFile(name: String): String = ... 307 | ``` 308 | 309 | ## String interpolation 310 | 311 | In Scala, you can concatenate strings and other values in the same way as in Java - using the `+` operator. However, there is a much nicer syntax to do it called string interpolation. It allows you to concisely embed some expressions inside a string literal. For example: 312 | 313 | ```scala 314 | println("My name is " + name + " and I am " + age + " years old.") 315 | ``` 316 | 317 | could be rewritten as: 318 | 319 | ```scala 320 | // note the 's' 321 | println(s"My name is $name and I am $age years old.") 322 | ``` 323 | 324 | In the example above, we have embedded references to simple identifiers inside the string literal. However, arbitrary expressions can be embedded. For example: 325 | 326 | ```scala 327 | val name = "fred" 328 | val age = 27 329 | // 'capitalize' converts first letter of a string to upper case 330 | println(s"My name is ${name.capitalize} and I am $age years old.") 331 | ``` 332 | 333 | There is a few reasons why the `s` is required at the beginning of a string literal: 334 | * String interpolations were introduced in Scala 2.10. It would severely break source compatibility with older versions if regular string literals suddenly became string interpolations. 335 | * `s` is only one of the available interpolators. It simply concatenates all arguments and string literal parts. But Scala provides a few other interpolators which do something different: 336 | * the `f` interpolator allows you to provide `printf`-style format to each embedded expression: 337 | 338 | ```scala 339 | val name = "Fred" 340 | val height = 1.8 341 | println(f"$name%s is $height%2.2f meters tall") 342 | ``` 343 | * the `raw` interpolator works just like `s` but it doesn't treat escape sequences 344 | 345 | ```scala 346 | println(raw"sth\t\nsth") // will print 'sth\t\nsth', \t and \n won't be escaped 347 | ``` 348 | `raw` interpolator can come in handly when defining regular expressions which have their own escaping - you can avoid having two levels of escaping. As an alternative to `raw` interpolator you can also use multiline string syntax (described below). 349 | 350 | * The real strength of string interpolations in Scala is that it's an extensible feature - it's possible to define custom string interpolators, which can have **arbitrary signatures** - an interpolation may decide how many arguments does it take, what are their types and what's the final result type (it doesn't have to be a string). For example, one may define a `json` interpolator which parses the string literal along with some spliced arguments into some Scala JSON representation. 351 | 352 | ### String interpolations and escaping 353 | 354 | There's an annoying difference in how scalac parses string interpolations with regard to treating escapes. Escape sequences in normal strings are treated during parsing of Scala code, so that in runtime there is no information left about it. With string interpolations it's not like that and it has some unpleasantly surprising consequences. 355 | 356 | The compiler leaves the job of treating escapes to be done in runtime by the actual implementations of string interpolations. At first sight this is good because each interpolation may decide if it wants to treat escapes or not. This way we have `s` and `f` which do treat escapes and the `raw` interpolation which doesn't do that. However, this also means that we cannot escape double quotes (`"`) in string interpolations. 357 | 358 | For example if you change a perfectly correct string literal `"sth\"more"` into a string interpolation `s"sth\"more"` it will suddenly stop compiling because the parser will think that the escaped double quote is actually the end of the string. This might cause compilation errors which are **very confusing** to the programmer unaware of this behaviour. 359 | 360 | Also, note that in order to avoid escaping, instead of using `raw` interpolator 361 | 362 | ## Multiline strings 363 | 364 | Scala has special syntax for string literals which may span multiple lines: 365 | 366 | ```scala 367 | val text = """some long 368 | multiline text 369 | I don't have to "escape" \ anything 370 | """ 371 | ``` 372 | 373 | Multiline strings don't escape any characters - new lines and all other special characters are included in the resulting string without change. Unfortunately, this has a drawback. In the example above, the string will contain all the whitespace present at the beginning of each line. Fortunately, Scala provides a special method on string that can strip these: 374 | 375 | ```scala 376 | val text = """some long 377 | |multiline text 378 | |I don't have to "escape" \ anything 379 | |""".stripMargin 380 | ``` 381 | 382 | The `stripMargin` method will search for `|` characters inside the string and strip each line to only the contents after `|`. 383 | 384 | It is also possible to define *multiline string interpolations*: 385 | 386 | ```scala 387 | val email = s"""Hello 388 | | 389 | |My name is $name and I'm $age years old. 390 | | 391 | |Best regards 392 | |$name 393 | |""".stripMargin 394 | ``` 395 | 396 | The triple-quote syntax is also useful even if your string doesn't have multiple lines. This is because inside triple quotes it's not necessary to treat escapes. Therefore, it's a good alternative to `raw` interpolator which avoids the problems with escaping double quote: 397 | 398 | ```scala 399 | val regex = """(\w)+"something""" 400 | ``` 401 | 402 | **However**, remember that string interpolations treat escapes in **runtime**, so if you change the above to string interpolation (e.g. `s"""(\w)+"something"""`) then it will **fail with runtime** with `InvalidEscapeException` when trying to interpret `\w` as an escape sequence! 403 | 404 | Opinionated conclusion: the fact that the compiler moves treatment of escape sequences in string interpolation to runtime was a really bad idea by language designers. -------------------------------------------------------------------------------- /chapters/Basic-functional-constructs.md: -------------------------------------------------------------------------------- 1 | # Basic functional constructs 2 | 3 | One of the most basic features of any functional language are lambdas, i.e. anonymous functions. It's a no surprise that Scala has them too. 4 | 5 | ## Function objects 6 | 7 | Scala is a purely object oriented language in the sense that every value is an object. This includes functions. There is a set of *traits* (something similar to Java interfaces), each one representing a function of some shape/arity. They can be implemented in Scala just like any other trait, but this generally looks similar to anonymous classes in Java and introduces a lot of boilerplate. Instead, there is a short lambda syntax to represent anonymous function objects. 8 | 9 | ### `Function0` 10 | 11 | The most primitive type of a function is a function that takes no arguments. Anonymous no-argument function can be declared in Scala with following syntax: 12 | 13 | ```scala 14 | val doSomething = () => println("stuff") 15 | ``` 16 | 17 | We're relying on type inference to deduce the type of `doSomething`, but we could make it explicit: 18 | 19 | ```scala 20 | // we have split it into two lines to make it more readable 21 | val doSomething: () => Unit = 22 | () => println("stuff") 23 | ``` 24 | 25 | `() => T` is special scala syntax to denote types of no-argument functions. It is equivalent to `Function0[T]`. If we look into the `Function0` trait, we'll see that it has the `apply` method: 26 | 27 | ```scala 28 | def apply(): R 29 | ``` 30 | 31 | As we know from previous section, `apply` is a magic method which here allows us to invoke our function like this: 32 | 33 | ```scala 34 | doSomething() 35 | ``` 36 | 37 | We're using the magic of `apply` to make the invocation look like we're calling `doSomething` method even though it's actually a call of `apply` method on `doSomething` object. 38 | 39 | #### Variance 40 | 41 | `Function0` has a nice property - it is *covariant*. This means that when `B` is a subtype of `A` then `() => B` is a subtype of `() => A`. Thanks to covariance, we can write: 42 | 43 | ```scala 44 | val produceString: () => String = 45 | () => "someString" 46 | 47 | val produceAnything: () => Any = 48 | produceString 49 | ``` 50 | 51 | Well, a function that produces a string is definitely a function that produces anything. This may seem like something very natural and obvious - how could this even work differently and why do we praise it so much? Well, try assigning a `Supplier` to `Supplier` in Java - no luck. Variance (or strictly speaking - *declaration site* variance) is a surprisingly complex feature even though it seems obvious in usage site. We'll talk more about it in some other chapter. 52 | 53 | ### `Function1` 54 | 55 | `Function1` trait represents the most commonly used function type - a function that takes a single argument. The full syntax to define anonymous `Function1` is as follows: 56 | 57 | ```scala 58 | val fun = (x: String) => x.toInt 59 | ``` 60 | 61 | This defines a function which takes a single string argument and parses it into an `Int`. Just like with `Function0`, we're relying on type inference, but we may be explicit about the type: 62 | 63 | ```scala 64 | val fun: String => Int = 65 | (x: String) => x.toInt 66 | ``` 67 | 68 | `String => Int` denotes a type of function from `String` to `Int`. This is equivalent to `Function1[String,Int]`. It's also important to remember that the `=>` symbol is right-associative. This means `A => B => C` means "a function from `A` that returns a function from `B` to `C`", i.e. `A => (B => C)`. 69 | 70 | Now that we explicitly declared the type of our function, we may omit the type declaration in lambda parameter: 71 | 72 | ```scala 73 | val parseInt: String => Int = 74 | x => x.toInt 75 | ``` 76 | 77 | In this case, the body of anonymous functions is simple enough so that we can shorten it even more: 78 | 79 | ```scala 80 | val parseInt: String => Int = _.toInt 81 | ``` 82 | 83 | Also, we could have used the underscore syntax even without the the explicit type declaration, but this time we need to declare the type on the underscore itself: 84 | 85 | ```scala 86 | val parseInt = (_: String).toInt 87 | ``` 88 | 89 | As you can see, Scala has multitude of different syntactic flavors for lambdas. 90 | 91 | #### `Function1` methods 92 | 93 | Now, let's look what we can do with our function. Here's what the `Function1` trait exposes: 94 | 95 | ```scala 96 | // assume function of type B => C 97 | def apply(b: B): C 98 | def compose[A](g: A => B): A => C 99 | def andThen[D](g: C => D): B => D 100 | ``` 101 | 102 | Of course, there's a magic `apply` method which allows us to call the function just like it was a method: 103 | 104 | ``` 105 | parseInt("123") 106 | ``` 107 | 108 | But there are also two other methods - `compose` and `andThen`. These two allow us to combine two functions into one. For example: 109 | 110 | ```scala 111 | val parseAndDivide = parseInt andThen (_ / 2) 112 | val parseAndMultiply = ((_: Int) * 2) compose parseInt 113 | ``` 114 | 115 | `compose` and `andThen` do the same thing - `compose` is equivalent to `andThen` with arguments flipped. 116 | 117 | #### Variance 118 | 119 | Just like `Function0`, `Function1` is covariant in its return type. If `B` is a subtype of `A` then function which returns `B` is a valid (subtype of) function that returns `A`. 120 | 121 | But there's something more going on here. `Function1` is also *contravariant* in its argument type, which means that a function which takes `A` as its argument is a (valid subtype) of function that takes `B` as its argument (note that this is a "reversed" covariance). For example: 122 | 123 | ```scala 124 | val consumeAnything: Any => Unit = 125 | println(_) 126 | 127 | val consumeString: String => Unit = 128 | consumeAnything 129 | ``` 130 | 131 | This is also very natural - a function that consumes anything is definitely a function that can consume a string. 132 | 133 | Covariance and contravariance can even work together: 134 | 135 | ```scala 136 | val consumeAnythingAndProduceString: Any => String = 137 | _.toString 138 | 139 | val consumeIntAndProduceCharSequence: Int => CharSequence = 140 | consumeAnythingAndProduceString 141 | ``` 142 | 143 | Don't worry if you don't understand how exactly variance works. It can become very complex sometimes, but you definitely don't need to think about it when working with functions. We're mentioning it here only because it's an important improvement over Java. As noted earlier, we'll get back to variance in a separate chapter. 144 | 145 | ### `Function2` 146 | 147 | Functions that take two arguments have similar syntax: 148 | 149 | ```scala 150 | // Scala introduces a `*` operator on strings which repeats a string given number of times 151 | val repeatUpper = (s: String, n: Int) => s.toUpperCase * n 152 | ``` 153 | 154 | Again, we may omit type declarations when explicitly stating the type of function: 155 | 156 | ```scala 157 | val repeatUpper: (String, Int) => String = 158 | (s, n) => s.toUpperCase * n 159 | ``` 160 | 161 | And again, this function is simple enough so that we can use underscore syntax: 162 | 163 | ```scala 164 | val repeatUpper: (String, Int) => String = 165 | _.toUpperCase * _ 166 | ``` 167 | 168 | Note that when using underscore syntax, each underscore represents different parameter. 169 | 170 | #### `Function2` methods 171 | 172 | Let's see what can we do with our two-argument function: 173 | 174 | ```scala 175 | // assuming function (A, B) => C 176 | def apply(a: A, b: B): C 177 | def curried: A => B => C 178 | def tupled: ((A, B)) => C 179 | ``` 180 | 181 | The magic `apply` method doesn't need explanation at this point. But we also have two strange converters: `curried` and `tupled`. 182 | 183 | `curried` transforms our two-argument function into a function that takes one argument and returns a function that takes the other argument and then returns final result. So, instead of taking both parameters at once, curried version takes them one by one, producing intermediate, *partially applied* function. 184 | 185 | `tupled` transforms our two-argument function into a single-argument function where the argument is a pair that contains our two original arguments. 186 | 187 | The difference between regular function and its curried and tupled version is mostly syntactical. You use them differently when calling them and in different situations one may be more convenient than the other. But other than that, all three variants are doing the same computation inside. 188 | 189 | #### Variance 190 | 191 | Variance in `Function2` works the same way as in `Function1`, except that `Function2` is contravariant in both its parameter types. 192 | 193 | ### `Function3` and beyond 194 | 195 | Scala defines function traits up to `Function22`. They all work pretty much the same way as `Function2`. The limit of 22 parameters is somewhat arbitrary and is caused by some kind of limitation of the JVM. Anyway, who would want to declare a function with over 20 parameters? 196 | 197 | ### More about underscore syntax 198 | 199 | In examples of `Function1` and `Function2`, we used underscore syntax to define anonymous functions: 200 | 201 | ```scala 202 | val parseInt: String => Int = _.toInt 203 | val repeatUpper: (String, Int) => String = _.toUpperCase * _ 204 | ``` 205 | 206 | This is the most concise syntax to define anonymous functions, but it has limitations. When we showed these examples, we said that we can use underscore syntax only because our functions are "simple" enough. But what does that mean, exactly? 207 | 208 | Underscore syntax cannot be used in following situations: 209 | * When we refer to function argument more than once in function body: 210 | 211 | ```scala 212 | val squared: Int => Int = x => x * x 213 | val squared2: Int => Int = _ * _ // error! 214 | ``` 215 | 216 | We can't do it because every underscore in function body represents separate argument. Scala compiler understands `_ * _` as `(x, y) => x * y` 217 | 218 | * When reference to argument is more deeply nested in function body: 219 | 220 | ```scala 221 | val printUpper: String => Unit = s => println(s.toUpperCase) 222 | val printUpper2: String => Unit = println(_.toUpperCase) // error! 223 | ```` 224 | 225 | This time it doesn't work because the compiler understands `println(_.toUpperCase)` as `println(s => s.toUpperCase)`. It thinks that we're passing a function to `println`. 226 | 227 | ### Turning methods into functions 228 | 229 | Very often we need a function that does nothing more than passing its arguments to some method: 230 | 231 | ```scala 232 | def addModulo10(x: Int, y: Int) = (x + y) % 10 233 | // the `reduce` method applies two-argument function consequently on all 234 | // elements of the vector until it has single value left 235 | val sumModulo10 = Vector(1,2,3).reduce((x,y) => addModulo10(x,y)) 236 | ``` 237 | 238 | In such situations, if the type of the function is clear from the context, then we can simply write the name of the method where the function is expected: 239 | 240 | ```scala 241 | val sumModulo10 = Vector(1,2,3).reduce(addModulo10) 242 | ``` 243 | 244 | Scala's automatic conversion of methods into functions is a transformation called *eta expansion*. 245 | 246 | However, sometimes the compiler doesn't have enough information to do this automatically. For example, in the snippet below we don't declare type of `val` and the compiler doesn't know that it has to treat the method as function: 247 | 248 | ```scala 249 | def addModulo10(x: Int, y: Int) = (x + y) % 10 250 | val reductor = addModulo10 // error! 251 | ``` 252 | 253 | We have a few options to fix this: 254 | ```scala 255 | val reductor1: (Int, Int) => Int = addModulo10 256 | val reductor2 = addModulo10(_, _) 257 | val reductor3 = addModulo10 _ 258 | ``` 259 | 260 | We already know the first two methods. But the third has some new syntax - method name with a space and underscore after it. That's simply another way to force the compiler to treat the method as function - a slightly shorter version of the lambda syntax in second option. 261 | 262 | ## Higher order functions 263 | 264 | A higher order function (or method) is a function that takes another function as its argument. 265 | 266 | Therefore, it's not a surprise that anonymous functions are mostly used when being passed to higher order methods. We've already used a few of these, e.g. `foreach` or `reduce`. In this section, we'll stay a bit more with them to discuss not only how to use them, but how to *define* them. 267 | 268 | There is a great number of HOFs available in the Scala collection API. Here are some examples of most basic ones: 269 | 270 | ```scala 271 | val nums = Vector(1,5,2,6,4,3,2,7).filter(_ < 5) // Vector(1,2,4,3,2) 272 | val ints = Vector("123", "456").map(_.toInt) // Vector(123,456) 273 | Vector("abc", "hello").foreach(println) // prints "abc" and "hello" 274 | ``` 275 | 276 | These are simple - they only take single argument (which is a function). However, when a HOF takes more than one argument, it tends to have a bit more peculiar signature. For example, let's look at the `foldLeft` method available in the Scala collection API: 277 | 278 | ```scala 279 | // A is the type of elements in the collection 280 | def foldLeft[B](z: B)(op: (B, A) => B): B 281 | ``` 282 | 283 | It takes two arguments - a value `z` of arbitrary type `B` and a binary function which is able to combine a value of `B` with collection element to produce another value of `B`. At the beginning, the function is applied on `z` and first element of the collection. Then we have another value of type `B` ("next" `z`) which is combined with second element of the collection. This goes on until we apply our function on every collection element and produce final value of type `B`. That value is then returned as the result of entire `foldLeft` invocation. 284 | 285 | Very simple example of `foldLeft` usage is to compute a sum of squares of every element in collection: 286 | 287 | ```scala 288 | val sumOfSquares = Vector(1,2,3,4).foldLeft(0)((sumSoFar, el) => sumSoFar + el * el) 289 | ``` 290 | #### Multiple parameter lists 291 | 292 | You may have already spotted that there's something strange about the signature of `foldLeft` - it takes two arguments, but each of them is passed in a separate pair of parentheses, i.e. each has a separate parameter list in method declaration. 293 | 294 | First of all, it may be surprising that Scala even allows something like this. And there's even more than that - `foldLeft` has two parameter lists, but we can define a method which takes arbitrary number of parameter lists, each one with arbitrary number of parameters (zero in particular). 295 | 296 | But why would we need something like this? There is a few good reasons for that: 297 | 298 | ##### Limitations of type inference 299 | 300 | This is probably the most important reason and the primary purpose for multiple parameter lists in `foldLeft`. We'll try to outline what kind of limitations we have in mind. Let's try to define our own `foldLeft` method that takes its argument in a *single* parameter list. For simplicity, let's assume that it works on a collection that we have in a local variable: 301 | 302 | ```scala 303 | val vec = Vector(1, 2, 3, 4) 304 | def vecFoldLeft[B](z: B, f: (B, Int) => B): B = vec.foldLeft(z)(f) 305 | 306 | vecFoldLeft(0, (sumSoFar, el) => sumSoFar + el * el) // error! 307 | ``` 308 | 309 | Unfortunately, when we try to compile it, we get an error: 310 | 311 | ``` 312 | error: missing parameter type 313 | vecFoldLeft(0, (sumSoFar, el) => sumSoFar + el * el) 314 | ^ 315 | ``` 316 | 317 | We have passed an `Int` value (zero) as the `z` parameter, so it should be clear for the compiler that `B` == `Int` and the type of `sumSoFar` parameter is `Int`. But unfortunately, it is not. The compiler cannot infer the type of one argument based on type of other argument in the same argument list. That is simply a somewhat arbitrary limitation of type inference algorithm used in Scala. 318 | 319 | Apparently, the problem disappears when we split the parameters into two separate parameter lists: 320 | 321 | ```scala 322 | def vecFoldLeft[B](z: B)(f: (B, Int) => B): B = vec.foldLeft(z)(f) 323 | vecFoldLeft(0)((sumSoFar, el) => sumSoFar + el * el) 324 | ``` 325 | 326 | This is because Scala type inference processes every argument list one after another. While doing so, it is able to use information obtained from previous argument lists to infer types in subsequent argument lists. In the example above, when the first argument list is processed, the compiler determines that `B` == `Int`. Then it becomes clear for it that the type of `f` is `(Int,Int) => Int` and no longer requires explicit type declaration for `sumSoFar`. 327 | 328 | Of course, we could also fix the problem by actually giving the `sumSoFar` parameter some explicit type. But it is better to force the inference to work how we want instead of giving it explicit information. 329 | 330 | ##### Nicer syntax with long functions 331 | 332 | Taking out the function parameter into separate parameter list also has slight aesthetical advantage. If our function is long, we can pass it like this: 333 | 334 | ```scala 335 | Vector(1,2,3).foldLeft(0) { (sumSoFar, el) => 336 | // some long code here 337 | sumSoFar + el * el 338 | } 339 | ``` 340 | 341 | instead of: 342 | 343 | ```scala 344 | // let's ignore type inference problems for this example 345 | Vector(1,2,3).foldLeft(0, (sumSoFar, el) => { 346 | // some long code here 347 | sumSoFar + el * el 348 | }) 349 | ``` 350 | 351 | That's minor syntactical advantage - we don't need to wrap our function into both curly braces and parentheses. 352 | 353 | *Summary*: when declaring higher order methods which take more than one argument, it is good to place the function argument in a separate parameter list and put it at the end. 354 | 355 | ##### Slightly nicer partial application 356 | 357 | When you expect your higher order method to be partially applied, it is good to move the parameters likely to be NOT supplied when partially applying into the separate parameter list and put it at the end. For example, the following snippet: 358 | 359 | ```scala 360 | def takeManyArgs(i: Int, s: String, d: Double, vi: Vector[Int], ls: List[String]) = ??? 361 | val takeOnlySomeArgs = takeManyArgs(42, "stuff", _, _, _) 362 | ``` 363 | 364 | could become: 365 | 366 | ```scala 367 | def takeManyArgs(i: Int, s: String)(d: Double, vi: Vector[Int], ls: List[String]) = ??? 368 | val takeOnlySomeArgs = takeManyArgs(42, "stuff") _ 369 | ``` 370 | 371 | which may be a bit more concise. 372 | 373 | Also, by splitting your parameters into separate lists, you kind-of "suggest" that your HOF is likely to be partially applied. 374 | 375 | ## Closures 376 | 377 | **TODO** 378 | 379 | ## By-name parameters 380 | 381 | Normally, when you pass some expression as an argument to some method, the expression is evaluated eagerly and its result value is passed to that method, which only then is invoked. However, Scala allows to alter this behavior with the so-called *by-name parameters*. 382 | 383 | Let's start with an example. Imagine we'd like to write our own implementation of `if-else` statement using a regular method. The method would have to take three parameters: the condition and two expressions - the first one evaluated when condition is true and the second one otherwise: 384 | 385 | ```scala 386 | // the method is parameterized (generic) with arbitrary type T 387 | def ifelse[T](condition: Boolean, whenTrue: T, whenFalse: T): T = 388 | if(condition) whenTrue else whenFalse 389 | ``` 390 | 391 | However, this implementation is not good. If we were to call: 392 | 393 | ```scala 394 | val x: Int = fetchSomeInt() 395 | ifelse(x < 5, println("x < 5"), println("x >= 5")) 396 | ``` 397 | 398 | the output would be: 399 | 400 | ``` 401 | x < 5 402 | x >= 5 403 | ``` 404 | 405 | Both `x < 5` and `x >= 5` have been printed. This is because the `println` invocations have been evaluated *eagerly*, before the `ifelse` method was called. We need to find some way to defer the evaluation until the condition is checked. 406 | 407 | #### Solution with `Function0` 408 | 409 | Instead of passing eagerly evaluated values to our method, we could use no-argument functions: 410 | 411 | ```scala 412 | def ifelse[T](condition: Boolean, whenTrue: () => T, whenFalse: () => T): T = 413 | if(condition) whenTrue() else whenFalse() 414 | ``` 415 | 416 | We need to wrap passed expressions into no-arg functions, too: 417 | 418 | ```scala 419 | ifelse(x < 5, () => println("x < 5"), () => println("x >= 5")) 420 | ``` 421 | 422 | This will work as intended - the no-argument functions are going to be invoked only after checking the condition and it is guaranteed that only one of them will be invoked. 423 | 424 | #### Solution with by-name params 425 | 426 | By-name arguments work very similarly to no-argument functions, but have more concise syntax and are treated by the Scala compiler a bit differently. Using by-name arguments, our `ifelse` method could be implemented like this: 427 | 428 | ```scala 429 | def ifelse[T](condition: Boolean, whenTrue: => T, whenFalse: => T): T = 430 | if(condition) whenTrue else whenFalse 431 | ``` 432 | 433 | The `=> T` part is a new syntax, previously not used in this guide. It denotes that the parameter is a *by-name* parameter. This is pretty much the same as if the parameter was a no-argument function, except that we don't need to wrap the passed expression explicitly into a function. We could pass the arguments as if it was a regular method: 434 | 435 | ```scala 436 | ifelse(x < 5, println("x < 5"), println("x >= 5")) 437 | ``` 438 | 439 | Passed expressions will still be evaluated on-demand, after checking the condition. 440 | 441 | #### Multiple evaluation 442 | 443 | It is very important to remember that by-name arguments are re-evaluated **every single time** they are referred to. For example, the following code will print `"Hello"` twice: 444 | 445 | ```scala 446 | def doTwice(code: => Any): Unit = { 447 | code 448 | code 449 | } 450 | doTwice(println("Hello")) 451 | ``` 452 | 453 | Therefore, by-name arguments cannot be treated simply as "lazy" arguments. 454 | 455 | Multiple evaluation can sometimes be useful. For example, arrays in Scala can be created using the `Array.fill` method which takes array size and initial value for every element as its arguments. The initial value is, however, taken as a by-name parameter. Thanks to that, we could for example create an array of empty mutable Java lists, where every list is a separate instance: 456 | 457 | ```scala 458 | // 10 lists will be created 459 | val lists = Array.fill(10)(new java.util.ArrayList[String]) 460 | ``` 461 | 462 | #### Use by-name params carefully 463 | 464 | By-name arguments provide nicer syntax than no-argument functions, but that syntax can also be dangerous. When looking at method invocation, we can never be sure if its parameters are regular or by-name until we look at method signature. This can lead to very tricky errors. 465 | 466 | Someone could break the program simply by extracting to a local variable an expression passed as by-name argument, causing it to be evaluated eagerly. Things could also go bad when the by-name argument is evaluated more than once by its method or saved somewhere to be evaluated later. 467 | 468 | When defining methods with by-name arguments, we recommend to adhere to following rules: 469 | 470 | * method name and purpose should make it clear that its arguments are (or may be) by-name arguments, so that this is apparent in the call site 471 | * by-name arguments should not be evaluated more than once inside the method, unless its name and usage make it very clear that they are 472 | * by-name arguments should not be passed for evaluation outside of its method, unless - again - method name and usage make it very clear that they are 473 | 474 | In other words, you should design your API so that someone who reads code that uses it sees clearly that some arguments are passed as by-name. 475 | 476 | If you have doubts whether it's safe to use by-name arguments, you can always fall back to no-argument functions. The difference between them only exists in compile time - in runtime by-name arguments are represented as `Function0` objects. 477 | 478 | ## Java interoperability 479 | 480 | **TODO** -------------------------------------------------------------------------------- /chapters/Classes-and-objects.md: -------------------------------------------------------------------------------- 1 | ## Classes and objects 2 | 3 | #### Classes 4 | 5 | As we have already seen in many examples in this tutorial - classes in Scala are defined in a standard way - with the `class` keyword and instantiated with the `new` keyword: 6 | 7 | ```scala 8 | // we can omit braces if class has empty body 9 | class MyClass 10 | // we can omit parens just like for methods with no params 11 | new MyClass 12 | ``` 13 | 14 | #### Objects 15 | 16 | We've also seen `object`s. They're essentially singleton classes: 17 | 18 | ```scala 19 | object Utilities { 20 | def utilMethod(param: Int): String = ??? 21 | } 22 | ``` 23 | 24 | Objects can have everything that a regular class has - methods, fields, inner classes etc. Also just like classes, they can inherit, implement and override. 25 | They are often used as namespaces for "static" functions and constants. 26 | 27 | Because each object is a sole instance of its own (unnamed) class, that instance can be referred to. That's why we can do everything with an object that we can do with a regular value, i.e. we can pass an `object` to a method: 28 | 29 | ```scala 30 | object SomeObject 31 | object Test { 32 | println(SomeObject) 33 | } 34 | ``` 35 | 36 | It's also important to remember that the sole instance of `object` is lazily initialized upon first access. 37 | 38 | ### Primary constructor 39 | 40 | Constructors in Scala work somewhat differently than in Java. Every Scala class has a *primary constructor*. The code of that constructor is simply the body of the class. For example: 41 | 42 | ```scala 43 | class MyClass { 44 | println("Doing some work in primary constructor!") 45 | } 46 | ``` 47 | 48 | Of course, class body may also contain members (methods, fields, etc.). This way it may actually become an arbitrary mix of class member definitions and pieces of primary constructor code: 49 | 50 | ```scala 51 | class MyClass { 52 | println("Creating MyClass!") 53 | 54 | val name: String = { 55 | println("Initializing name!") 56 | "John" 57 | } 58 | 59 | println("Finished creating MyClass!") 60 | } 61 | ``` 62 | 63 | The order in which pieces of primary constructor code and member initializers execute reflects their order in the source code, i.e. `new MyClass` would print: 64 | 65 | ``` 66 | Creating MyClass! 67 | Initializing name! 68 | Finished creating MyClass! 69 | ``` 70 | 71 | **NOTE**: `objects` also have a primary constructor, but (for obvious reasons) cannot take constructor parameters 72 | 73 | #### Primary constructor local variables 74 | 75 | The fact that body of primary constructor is mixed with member declarations brings some annoyances: you cannot use local variables in primary constructor code. Every `val` or `var` that you declare will become a member of the class (with underlying field in bytecode) and not just a local variable: 76 | 77 | ```scala 78 | class C { 79 | // that's a public member, not a local variable! 80 | var x: Int = 0 81 | } 82 | ``` 83 | 84 | This may not always that bad - the only problem with it is that there will be completely unnecessary field in the compiled bytecode that will hold a useless value. This may cause unwanted memory usage but if you don't care you can just declare it as `private` to not expose it outside of your class and be happy with it. However, there are some techniques that could still avoid the problem. 85 | 86 | If you need a local variable during computation of initial value of a `val` or `var`, you can assign a block to that field and put the local variable inside that block: 87 | 88 | ```scala 89 | class C { 90 | val someValue = { 91 | var localVar: Int = 0 92 | // some computations here ... 93 | someResultExpression 94 | } 95 | } 96 | ``` 97 | 98 | OK, but what if we need a the same local variable in order to compute initial values for *two* `val`s? Here we can use multi-value assignment (see pattern matching discussion for more details): 99 | 100 | ```scala 101 | class C { 102 | val (someValue, someOtherValue) = { 103 | var localVar: Int = 0 104 | // some computations here ... 105 | (firstResult, secondResult) 106 | } 107 | } 108 | ``` 109 | 110 | This is not ideal (e.g. we cannot document each `val`) but still better than anything. 111 | 112 | #### Primary constructor parameters 113 | 114 | The primary constructor may, of course, take some parameters. For this, we use a syntax that looks as if the class itself "takes" some parameters: 115 | 116 | ```scala 117 | class MyClass(myName: String) { 118 | println(s"My name is $myName") 119 | } 120 | ``` 121 | 122 | Just like methods, the primary constructor may take multiple parameter lists. 123 | 124 | The nice thing about primary constructor parameters is that they may be referred to anywhere in the class definition - not just in the constructor code. For example, we can define a method that uses primary constructor parameter: 125 | 126 | ```scala 127 | class MyClass(myName: String) { 128 | def printName(): Unit = println(myName) 129 | } 130 | ``` 131 | 132 | On bytecode level, Scala compiler will automatically turn such constructor parameters into class fields so that they can be accessed even after the primary constructor finished its execution. 133 | 134 | Primary constructors may also have access modifiers. For example: 135 | 136 | ```scala 137 | class MyClass private(myName: String) 138 | ``` 139 | 140 | This syntax may look somewhat odd when the constructor has no params and even more odd when the class has empty body: 141 | 142 | ```scala 143 | // private primary constructor with no params on class with empty body 144 | class MyClass private 145 | 146 | // this looks like "private inheritance" but it's just private constructor 147 | class MyOtherClass private extends Object 148 | ``` 149 | 150 | Access modifiers (`private`, `protected` etc) will be described in detail later in this section. 151 | 152 | You can easily turn primary constructor parameters to also be members of your class by adding the `val` or `var` keyword: 153 | 154 | ```scala 155 | class MyClass(val myName: String) 156 | // now myName is a constructor param but can also be accessed from outside! 157 | new MyClass().myName 158 | ``` 159 | 160 | Such constructor-parameter-members can have all the modifiers that normal class members can - they can be `private`, `final`, implement or override something, have annotations etc. 161 | 162 | #### Auxiliary constructors 163 | 164 | Apart from the primary constructor, you may also define *auxiliary constructors*. However - every auxiliary constructor must call some other (previously defined) constructor as its first instruction. This way the primary constructor must *always* be invoked eventually. 165 | 166 | The syntax for auxiliary constructors is as follows: 167 | 168 | ```scala 169 | class MyConnection(hostPort: String) { 170 | // simply delegate to primary constructor 171 | def this(host: String, port: Int) = this(s"$host:$port") 172 | 173 | def this(port: Int) = { 174 | // delegate to other constructor, but do something more afterwards 175 | this("localhost", port) 176 | println("No host given, assuming localhost!") 177 | } 178 | } 179 | ``` 180 | 181 | Auxiliary constructors are similar to overloaded constructors in Java. However, note that in Java they are often used to provide default values for some other constructors' parameters. In Scala this can be achieved much easier with default parameters. 182 | 183 | For example, instead of writing: 184 | 185 | ```scala 186 | class MyClass(name: String) { 187 | def this() = this("DefaultName") 188 | } 189 | ``` 190 | 191 | we can simply do this: 192 | 193 | ```scala 194 | class MyClass(name: String = "DefaultName") 195 | ``` 196 | 197 | The difference between the two is that the first variant is more usable from Java code - Java cannot easily call Scala methods or constructors which have default parameter values. 198 | 199 | ### Members 200 | 201 | Class members are divided into two groups - *term members* and *type members*. 202 | Term members are members which represent values - `val`s, `lazy val`s, `var`s, `def`s and `object`s. 203 | Type members are members which represent types - (inner) `class`es, `trait`s, `type` aliases and abstract `type`s. 204 | 205 | #### `def` members - methods 206 | 207 | These are just methods. We have already seen multiple examples of them. 208 | 209 | #### `val` members 210 | 211 | When a member of some class is a `val`, it represents a field and a method at the same time. Every `val` member is backed by a field in bytecode, but the field is private and every access to that field goes through an automatically generated getter - a method with the same name as the `val`. 212 | 213 | For example, this class: 214 | 215 | ```scala 216 | class Klass { 217 | val x = 5 218 | } 219 | ``` 220 | 221 | yields bytecode equivalent to this Java class: 222 | 223 | ```java 224 | public class Klass 225 | private final int x = 5; 226 | public int x() { 227 | return this.x; 228 | } 229 | } 230 | ``` 231 | 232 | You can also request that `scalac` generates a Java-like getter for your `val` using the `@BeanProperty` or `@BooleanBeanProperty` annotation, which is nice if you want your class to be consumed by one of the many Java frameworks that use reflection to inspect JavaBean classes. 233 | 234 | ```scala 235 | import scala.beans.BeanProperty 236 | 237 | class Klass { 238 | // will cause getX() method to be generated 239 | @BeanProperty val x = 5 240 | 241 | // will cause isGood() method to be generated 242 | @BooleanBeanProperty val good = true 243 | } 244 | ``` 245 | 246 | #### `lazy val` members 247 | 248 | `lazy val`s are just like `val`s except that they are initialized lazily, when they're first referred to. The initialization logic is implemented in a thread-safe manner, so you can safely refer to a `lazy val` from multiple threads without worrying about seeing uninitialized value or running initialization code multiple times. 249 | 250 | Thread safety is ensured by synchronization during initialization and volatile access (so, no unnecessary synchronization happens after the field has already been initialized). 251 | 252 | #### `var` members 253 | 254 | `var` members represent mutable fields. In bytecode, they are backed by a private field, a getter method (the same way as for `val`s) plus an additional *setter* method. The setter method uses peculiar name mangling - if your `var` is named `someVar`, its setter method will be `someVar_=`. This method is always used when someone assigns a new value to the `var`. Here's an example of Scala class with `var` member and a Java class that would compile to the same bytecode: 255 | 256 | ```scala 257 | class Klass { 258 | var number: Int = 0 259 | } 260 | ``` 261 | 262 | ```java 263 | public class Klass { 264 | private int number = 0; 265 | public number() { return this.number; } 266 | // number_$eq is a name-mangled version of number_= 267 | public number_$eq(int number) { this.number = number; } 268 | } 269 | ``` 270 | 271 | The synthetic setter can be called directly, e.g. 272 | 273 | ```scala 274 | val k = new Klass 275 | // following two lines are equivalent 276 | k.number = 10 277 | k.number_=(10) 278 | ``` 279 | 280 | As with `val`s, you can use `@BeanProperty` and `@BooleanBeanProperty` to request generation of JavaBean-style getter and **setter** for your `var` member. 281 | 282 | #### Manually defined accessors 283 | 284 | If you have a `var`, you cannot affect how its accessor methods are implemented. However, you can define a getter and a setter without a `var` at all. For example: 285 | 286 | ```scala 287 | class Klass { 288 | def number: Int = { /* get from somewhere */ } 289 | def number_=(n: Int): Unit = { /* set to somewhere */ } 290 | } 291 | ``` 292 | 293 | If you define such a pair, you can use it as if it was a `var`: 294 | 295 | ```scala 296 | val k = new Klass 297 | k.number = 10 298 | println(k.number) 299 | k.number += 5 300 | ``` 301 | 302 | **NOTE**: `k.number = 10` is automatically translated to `k.number_=(10)` only if **both** getter and setter are declared. Just declaring the setter is not enough. 303 | 304 | ### Access modifiers 305 | 306 | **TODO** 307 | 308 | ### Inner classes and objects 309 | 310 | #### Inner classes 311 | 312 | You can define a class inside another class (or object). Inner classes are bound to particular instance of its enclosing class (or object) and have access to its members. 313 | 314 | Instances of an inner class can be created only when having an instance of the outer class. For example: 315 | 316 | ```scala 317 | class Outer { 318 | val x = 5 319 | 320 | class Inner { 321 | // inner can access Outer's members 322 | println(x) 323 | } 324 | 325 | new Inner // will be bound to Outer's `this` instance 326 | } 327 | 328 | object Test { 329 | val outer = new Outer 330 | new outer.Inner // will be bound to 'outer' object 331 | } 332 | ``` 333 | 334 | #### Referring to outer instance 335 | 336 | If an inner class or object needs to refer to its outer class instance, it can use the `Outer.this` syntax: 337 | 338 | ```scala 339 | class Outer { 340 | class Inner { 341 | def printOuter(): Unit = println(s"My outer instance is ${Outer.this}") 342 | } 343 | } 344 | ``` 345 | 346 | However, there is an additional syntax that allows us to give an alias to outer instance which can be used in inner instances: 347 | 348 | ```scala 349 | class Outer { outer => 350 | class Inner { 351 | def printOuter(): Unit = println(s"My outer instance is $outer") 352 | } 353 | } 354 | ``` 355 | 356 | **NOTE**: this is one of the uses of more general syntax called *self-type annotation* but we won't discuss this in detail here 357 | 358 | #### Path dependent types 359 | 360 | Inner classes in Scala are also typed more strictly than in Java. Two instances of the same inner class bound to different instances of outer class have incompatible types with each other: 361 | 362 | ```scala 363 | class Outer { 364 | class Inner 365 | def takeInner(inner: Inner): Unit = ??? 366 | } 367 | ``` 368 | 369 | then: 370 | 371 | ```scala 372 | val o1 = new Outer 373 | val o2 = new Outer 374 | 375 | // error! - o1.Inner and o2.Inner are incompatible 376 | o2.takeInner(new o1.Inner) 377 | ``` 378 | 379 | Also because of this strict behaviour of Scala, there's no such type as `Outer.Inner` (that's how you would refer to it in Java). If you write `o.Inner` then `o` must be a `val` (or `lazy val` or `object`) that is an instance of `Outer`. Such types are called *path-dependent types*. 380 | 381 | If you want to express a type of inner class which doesn't care about the outer object, Scala lets you denote it as `Outer#Inner`, which means `Inner` instance of any `Outer` instance. 382 | 383 | ```scala 384 | class Outer { 385 | class Inner 386 | def takeWhoeversInner(inner: Outer#Inner): Unit = ??? 387 | } 388 | ``` 389 | 390 | then: 391 | 392 | ```scala 393 | val o1 = new Outer 394 | val o2 = new Outer 395 | 396 | // now fine 397 | o2.takeWhoeversInner(new o1.Inner) 398 | ``` 399 | 400 | **NOTE**: the `Outer#Inner` syntax and more strict typing of inner classes applies also to Java classes when using them in Scala code. 401 | 402 | #### Inner objects 403 | 404 | Inner objects are also bound to its outer class instance. And since each object is a singleton, it means that there's exactly one inner object instance per outer class instance (i.e. a singleton in scope of its outer instance - there may be many instances overall). 405 | 406 | It's important to remember that just like toplevel objects, inner objects are lazily initialized. 407 | 408 | #### Inner classes and objects of objects 409 | 410 | Objects can also have inner classes and objects - they work the same way as for outer classes, but of course typing constraints are no longer relevant because there's only one possible outer instance. 411 | 412 | #### Note about `trait`s 413 | 414 | Scala traits can also be inner to objects, classes and other traits and traits themselves can also have inner classes, objects and traits. This was not mentioned earlier because traits require a separate chapter of their own. 415 | 416 | ### Statics and companion objects 417 | 418 | **TODO** 419 | 420 | ### Inheritance and overriding 421 | 422 | **TODO** 423 | 424 | Implementing table: 425 | 426 | ``` 427 | abstract def val var 428 | concrete 429 | def yes no no 430 | val yes yes no 431 | lazy val yes yes no 432 | var yes no yes 433 | object yes yes no 434 | ``` 435 | 436 | Overriding table: 437 | 438 | ``` 439 | overridden def val lazy val var object 440 | overriding 441 | def yes no no no no 442 | val yes yes no no no 443 | lazy val yes no yes no no 444 | var pair no no no no 445 | object yes yes no no no 446 | ``` 447 | 448 | ### Type members 449 | 450 | **TODO** 451 | 452 | * constructors - primary, auxiliary 453 | * *local variables annoyance* 454 | * generics, existentials 455 | * methods, full syntax 456 | * abstract members, overriding 457 | * vals, lazy vals, vars, *bean properties* 458 | * inner classes and objects 459 | * no statics, companion objects, import annoyance 460 | * inner classes and objects 461 | * self alias 462 | 463 | -------------------------------------------------------------------------------- /chapters/Dissection-of-Hello-World.md: -------------------------------------------------------------------------------- 1 | # Dissection of Hello World 2 | 3 | ```scala 4 | object Main { 5 | def main(args: Array[String]): Unit = { 6 | println("Hello") 7 | } 8 | } 9 | ``` 10 | 11 | ## Objects 12 | 13 | Scala's `object` creates a singleton. In above example, the identifier `Main` refers to the singleton instance itself, not to its class. This means that `Main` can be used as a regular value, e.g. it can be passed as an argument to some function. 14 | 15 | ## Method definition 16 | 17 | Our singleton class has one method called `main`. As we can see, method definition syntax differs from Java: 18 | * method declaration or definition is denoted by the `def` keyword, followed by name of the method 19 | * arguments are declared with `name: Type` syntax 20 | * formal argument list is followed by return type declaration, in our example it is the `: Unit` part 21 | * return type declaration is followed by `=` sign and a method body 22 | 23 | ### The `Unit` type 24 | 25 | Scala's `Unit` type is roughly equivalent to Java's `void`, i.e. it is used to denote methods that do not return any meaningful value. Scala methods that return `Unit` are seen by Java as methods with `void` return type and vice versa - Java methods with `void` return type as seen by Scala as methods returning `Unit`. 26 | 27 | However, there is an important difference between Java's `void` and Scala's `Unit`: the `Unit` type actually *has* a value. That value is called "unit" and is denoted in Scala as `()`. Every method which has `Unit` return type actually returns `()`. So you could theoretically create a (useless) local variable of type `Unit` and assign something to it, e.g. 28 | 29 | ```scala 30 | val foo: Unit = () 31 | val bar: Unit = println("stuff") 32 | ``` 33 | 34 | I haven't shown how to declare local variables yet, but I hope the above example is self-explanatory. We also see that the `println` method has `Unit` return type. 35 | 36 | ## Method body 37 | 38 | The body of our `main` method looks mostly similar to how you would do it in Java - we have a block with some code inside. The only minor difference is that the `=` sign is required before the body. 39 | However, the apparent similarity is a bit coincidental in our example. 40 | 41 | ### Blocks 42 | 43 | Blocks, denoted by curly braces, in Scala look similar to blocks in Java, but are treated a bit differently. Java blocks are strictly pieces of *code* - that is, you can't assign a block to a variable or pass a block as a method parameter. In Scala, however, this is possible, because blocks are *expressions*, i.e. they evaluate to some value. 44 | 45 | The value "returned" from a block is the value of last expression in that block. For example: 46 | 47 | ```scala 48 | { 49 | println("something") 50 | handleStuff() 51 | 42 52 | } 53 | ``` 54 | 55 | The block above will evaluate to `42` and therefore, can be assigned to variable, passed as argument, etc. 56 | 57 | ### Method body is an expression 58 | 59 | In general, method definition expects an *expression* after the `=` sign. The reason why we were able to put a block as a body of `main` is because a block is also an expression. But since our block has only one statement inside, we could shorten our `main` method definition: 60 | 61 | ```scala 62 | def main(args: Array[String]): Unit = println("Hello") 63 | ``` 64 | 65 | Note that this is possible also thanks to the fact that `println` actually returns a value and can be "assigned" as a return expression of a method. 66 | 67 | All of this also explains why the `=` sign is used. It more clearly denotes the fact that method body is an expression which always evaluates to something rather than a block of imperative code which may optionally return a value. 68 | 69 | ## Type inference 70 | 71 | Scala is able to tell the return type of a method based on its body. For our `main` method, Scala compiler is able to deduce that since `println` returns `Unit`, then `main` also returns `Unit`. Thanks to that, we can omit the return type declaration and make the code even shorter: 72 | 73 | ```scala 74 | def main(args: Array[String]) = println("Hello") 75 | ``` 76 | 77 | Type inference is one of the key features of Scala that allows writing concise code. As we will see later, it works in many other contexts, not just in method definitions. 78 | 79 | ## Procedure syntax 80 | 81 | There is also one more syntax variant in which we could write our `main` method. It looks like this: 82 | 83 | ```scala 84 | def main(args: Array[String]) { 85 | println("Hello") 86 | } 87 | ``` 88 | 89 | We have omitted return type declaration and the `=` sign and put our method body in a block. Such syntax can be used for methods which return `Unit` (procedures). However, it is not recommended and will probably be removed from future versions of scala. We present it here for the sake of completeness - if you're going to read existing Scala code, you'll probably see this syntax frequently. 90 | 91 | ## Parameter list 92 | 93 | We have declared our `main` method to take a single parameter named `args` which is an array of strings. There are a few differences from Java that are important here: 94 | * Obviously, parameter declaration syntax is different with type coming after the name 95 | * Arrays in Java are treated specially, but in Scala `Array` is simply a class that takes a type parameter, just like e.g. `java.util.List[String]`. Scala uses the square brackets `[]` to denote type parameters, as opposed to Java which uses angle brackets `<>`. 96 | 97 | ## Semicolons 98 | 99 | You may have noticed that our `println("Hello")` call does not end with a semicolon. Semicolons are optional in Scala. They are typically used only to explicitly separate two statements inside a block when they are on the same line, e.g. `{ println("Hello"); println("World") }`. 100 | 101 | ## The `Predef` object 102 | 103 | You may recall that in Java, there is no way to define "global" methods. Every method must be a member of some class. In Scala, we have the same rule - every method must come from a class or object. But you may have also noticed that we called our `println` method like it was global. 104 | 105 | The `println` method actually comes from an object `scala.Predef` in the Scala standard library which contains some basic utilities like console operations. This object is treated specially by the Scala compiler - all its members are automatically visible everywhere. That is why we were able to call `println` directly, without having to write `Predef.println` or `scala.Predef.println`. 106 | 107 | ## Runtime 108 | 109 | If you're not using an IDE or build tool (which you definitely should), you can compile our example by saving it in a `Main.scala` file (file name is arbitrary) and invoking `scalac Main.scala`. We will dig a bit into the bytecode to see how Scala compiler encodes `object`s. 110 | 111 | You will see that two classfiles have been generated: `Main.class` and `Main$.class`. The `Main$` is the class that actually implements the singleton. Let's see what's inside it: 112 | 113 | ``` 114 | $ javap -private -c Main\$ 115 | Compiled from "Main.scala" 116 | public final class Main$ { 117 | public static final Main$ MODULE$; 118 | 119 | public static {}; 120 | Code: 121 | 0: new #2 // class Main$ 122 | 3: invokespecial #12 // Method "":()V 123 | 6: return 124 | 125 | public void main(java.lang.String[]); 126 | Code: 127 | 0: getstatic #19 // Field scala/Predef$.MODULE$:Lscala/Predef$; 128 | 3: ldc #21 // String Hello 129 | 5: invokevirtual #25 // Method scala/Predef$.println:(Ljava/lang/Object;)V 130 | 8: return 131 | 132 | private Main$(); 133 | Code: 134 | 0: aload_0 135 | 1: invokespecial #29 // Method java/lang/Object."":()V 136 | 4: aload_0 137 | 5: putstatic #31 // Field MODULE$:LMain$; 138 | 8: return 139 | } 140 | ``` 141 | 142 | We can see that: 143 | * The static initializer of `Main$` invokes constructor. 144 | * The constructor assigns itself to the `MODULE$` static field. This is the singleton instance of our class. 145 | * The `main` method is a regular method, not a static one. 146 | 147 | However, there is also a second class generated - `Main`. Let's see what's inside. 148 | 149 | ``` 150 | $ javap -private -c Main 151 | Compiled from "Main.scala" 152 | public final class Main { 153 | public static void main(java.lang.String[]); 154 | Code: 155 | 0: getstatic #16 // Field Main$.MODULE$:LMain$; 156 | 3: aload_0 157 | 4: invokevirtual #18 // Method Main$.main:([Ljava/lang/String;)V 158 | 7: return 159 | } 160 | ``` 161 | 162 | We can see that the `Main` class also contains the `main` method. However, this one is static and simply forwards the call to `Main$.MODULE$.main`. Thanks to this static forwarder generated by `scalac`, we can run our program simply by writing: 163 | 164 | ``` 165 | $ scala Main 166 | Hello 167 | ``` 168 | -------------------------------------------------------------------------------- /chapters/Generics-and-type-members.md: -------------------------------------------------------------------------------- 1 | *Parametric polymorphism* is a key feature of statically typed programming languages. It is essential for writing expressive, typesafe and reusable code. 2 | 3 | Parametric polymorphism found in Java and Scala is usually known as *generics*. It allows various declarations and definitions to take *type parameters*. In Java, classes, interfaces and individual methods can be made generic by taking type parameters. In Scala, type parameters may be taken by methods, classes, traits and also type aliases and type members. 4 | 5 | ### Type parametrization - basic syntax 6 | 7 | Java uses angle brackets (`<>`) for denoting generics - Scala changes them to square brackets (`[]`). 8 | 9 | Here's an example generic method in Scala - it takes a value and duplicates it into a pair: 10 | 11 | ```scala 12 | def double[T](value: T): (T,T) = (value, value) 13 | ``` 14 | 15 | Here, type parameter `T` serves to express the fact that return type of a method is determined by the type of `value` parameter. This way the method is actually *polymorphic* - you can think about this as if there is a separate method for every type `T`. 16 | 17 | Here's another example: 18 | 19 | ```scala 20 | def listOfTwo[T](first: T, second: T): List[T] = List(first, second) 21 | ``` 22 | 23 | In this example, we not only express the fact that return type is determined by type of arguments but we also express a type constraint - arguments may be of any type but both of them must be of the same type. 24 | 25 | Above examples show generic methods. The same syntax is used to define generic classes and traits: 26 | 27 | ```scala 28 | class Box[T](var boxedValue: T) 29 | trait Callable[T] { 30 | def call(): T 31 | } 32 | ``` 33 | 34 | We'll talk about type aliases and abstract type members later. 35 | 36 | ### Bounds 37 | 38 | Type parameter may be constrained by type bounds. Type bounds define that given type parameter must always be either a subtype or supertype of some other type. Java uses `extends` and `super` keywords for type bounds. Scala invents its own pair of symbols for that, `<:` (*upper* bound) and `>:` (*lower* bound): 39 | 40 | A good technique to memorize that `<:` corresponds to `extends` and `>:` corresponds to `super` is to think of these as "less" and "greater" operators. For example, `String` is a subtype of `AnyRef`. This means that every `String` is an `AnyRef` but not vice versa. In other words, set of `String`s is contained in the set of `AnyRef`s, so `String` is "less" than `AnyRef` and so `String <: AnyRef`. 41 | 42 | Here's a convenience method that closes a `Closeable` but does it in fluent manner, i.e. it returns its own argument: 43 | 44 | ```scala 45 | def closed[T <: Closeable](closeable: T): T = { 46 | closeable.close() 47 | closeable 48 | } 49 | ``` 50 | 51 | We want the method to retain the actual type of argument passed and that's why we need the type parameter in the first place, but we also need to constrain the parameter so that only `Closeable`s can be passed. That's how the above upper type bound expresses it. 52 | 53 | Lower bound can be used in the same manner. It is also possible for a type parameter to have *both* upper and lower bound. 54 | 55 | #### `<: AnyRef` vs `>: Null` 56 | 57 | A common mistake with bounds is when someone wants to express that the type parameter represents a nullable value. For example, if you wanted to write a method which returns `null` as a value of type parameter, it may seem natural to implement it like this: 58 | 59 | ```scala 60 | def nullIfFailed[T <: AnyRef](expr: => T): T = 61 | try expr catch { 62 | case NonFatal(_) => null 63 | } 64 | ``` 65 | 66 | The above is a method which tries to evaluate some expression of generic type `T` and if that evaluation fails with an exception, a `null` fallback value is returned. It may seem natural to express the fact that `T` is nullable by placing an upper `AnyRef` type bound on it. `AnyRef` is `java.lang.Object`, so it can be `null`, right? 67 | 68 | Well, not so much. If you try to compile the above method, you'll get: 69 | 70 | ``` 71 | error: type mismatch; 72 | found : Null(null) 73 | required: T 74 | case NonFatal(_) => null 75 | ^ 76 | ``` 77 | 78 | The type bound `<: AnyRef` is **NOT** the correct way to express "nullability", because: 79 | 80 | * There is a type `Nothing`. You'll recall that it's a special type that's a subtype of all other types but has no values. That means it is within the type bound `<: AnyRef` but at the same time `null` can't be returned where `Nothing` is expected. 81 | * `null` is also a correct value of type `Any`, but `Any` doesn't fall within the `<: AnyRef` type bound 82 | 83 | The correct way to do this is to place the *lower* bound of Scala special type `Null` (a subtype of all nullable types): 84 | 85 | ```scala 86 | def nullIfFailed[T >: Null](expr: => T): T = 87 | try expr catch { 88 | case NonFatal(_) => null 89 | } 90 | ``` 91 | 92 | ### Wildcards, existentials 93 | 94 | Like Java, Scala also has *wildcards* - parameterized types whose type parameters are unknown. Java expresses wildcards with a question mark `?`, e.g. `Set` while Scala uses underscore `_` for that (yet another meaning of underscore in Scala), e.g. `Set[_]`. Such type should be read as "a set of some unknown element type". 95 | 96 | Wildcard types may also have type bounds, e.g. `Set[_ <: Number]`. 97 | 98 | Thanks to the mechanism of *declaration site variance* (we'll talk about this later), wildcards in pure Scala code are much less frequently used than in Java. In fact, they're mostly needed for interoperability with Java, which doesn't have declaration site variance. 99 | 100 | For example, in Java stream API, the `filter` method has the following signature (translated from Java to Scala): 101 | 102 | ```scala 103 | trait Stream[T] { 104 | ... 105 | def filter(predicate: Predicate[_ >: T]): Stream[T] 106 | ... 107 | } 108 | ``` 109 | 110 | If we used Scala's `Function` instead of `java.util.function.Predicate`, the signature could look like: 111 | 112 | ```scala 113 | def filter(predicate: T => Boolean): Stream[T] 114 | ``` 115 | 116 | We don't need to use any wildcards thanks to the fact that `Function` is *contravariant* in its input type. We have briefly explained that in [Basic functional constructs](Basic-functional-constructs). We'll also talk more about variance in detail later. 117 | 118 | #### Java raw types 119 | 120 | For backwards compatibility reasons, Java retained the possibility of using *raw types*, e.g. `Set` instead of `Set`. Please refer to resources about Java for more detailed explanation of raw types. Here, we just mention them to point out that Scala does **not** have raw types. 121 | 122 | However, sometimes Scala compiler must understand Java signatures which contain raw types. It does so by translating them to wildcard types. For example, Java raw type `java.util.Set` will be seen by Scala compiler as `java.util.Set[_]`. 123 | 124 | #### Existential types 125 | 126 | Wildcard types are a special case of a more general concept called *existential types*. For example: 127 | 128 | ```scala 129 | type S = Set[_] 130 | ``` 131 | 132 | is actually a shorter version of: 133 | 134 | ```scala 135 | type S = Set[A] forSome { type A } 136 | ``` 137 | 138 | This should be read as "there *exists* type `A` for which `T` is `Set[A]`" - that's why they're called *existential* types. 139 | 140 | You may wonder what's the point of that much more verbose syntax. Sometimes one is forced to declare an existential type which is inexpressible in terms of wildcards. An example of such type would be `Map[T,T] forSome { type T }`. Note that this is something completely different from `Map[_,_]` which means `Map[K,V] forSome { type K; type V }` (each wildcard represents a separate unknown type param). 141 | 142 | Complex existential types are rarely used - usually only in some more complex cases of Java interoperability. You should generally avoid designing your code around them. 143 | 144 | ### Type members 145 | 146 | You may have noticed that Scala has a `type` keyword. It's used for defining *type aliases* and *type members*. 147 | 148 | #### Type aliases 149 | 150 | Type aliases are just what you think. You can use a `type` declaration to give a shorter or more meaningful name to some type: 151 | 152 | ```scala 153 | type IntPair = (Int, Int) 154 | ``` 155 | 156 | Type aliases may also be parameterized: 157 | 158 | ```scala 159 | type Mapping[A] = Map[A,A] 160 | type JList[A] = java.util.List[A] 161 | ``` 162 | 163 | Type aliases may be defined locally (inside arbitrary block of code) or be *type members* of a class, trait or object: 164 | 165 | ```scala 166 | class Klass { 167 | type IntPair = (Int, Int) 168 | } 169 | ``` 170 | 171 | #### Abstract type members 172 | 173 | Type members may also be abstract. A declaration of abstract type member looks like this: 174 | 175 | ```scala 176 | trait MyStack { 177 | type Elem 178 | 179 | def push(elem: Elem): Unit 180 | def pop(): Elem 181 | } 182 | ``` 183 | 184 | Abstract type may also have bounds. For example: 185 | 186 | ```scala 187 | trait NumberCollection { 188 | type Elem <: Number 189 | } 190 | ``` 191 | 192 | Such abstract type member can then be inherited and made less abstract by other traits or classes. For example, a subtrait of `MyStack` may apply an additional constraint on the type member (while still leaving it abstract): 193 | 194 | ```scala 195 | trait ObjectStack extends MyStack { 196 | type Elem <: AnyRef 197 | } 198 | ``` 199 | 200 | Finally, some class or trait may want to make the type member non-abstract, thus making it a type alias in the scope of that class or trait: 201 | 202 | ```scala 203 | trait StringStack extends ObjectStack { 204 | type Elem = String 205 | } 206 | ``` 207 | 208 | Quite a confusing property of abstract type members is the fact that even though they're abstract, they don't necessarily need to be "implemented". They may remain abstract even though they are members of non-abstract classes. For example, the following code compiles just fine: 209 | 210 | ```scala 211 | class Klass { 212 | type T 213 | } 214 | object Klass { 215 | new Klass 216 | } 217 | ``` 218 | 219 | However, the type member is not really useful in that example - there's no way to actually create any value of that abstract type. 220 | 221 | #### Type members vs type parameters 222 | 223 | Looking at our previous examples you may wonder what's the point of using type members for something that can be as easily expressed with plain generics: 224 | 225 | ```scala 226 | trait MyStack[E] { 227 | def push(elem: E): Unit 228 | def pop(): E 229 | } 230 | trait ObjectStack[E <: AnyRef] extends MyStack[E] 231 | ``` 232 | 233 | Type members are indeed in many ways orthogonal to type parameters - they are often two ways of doing the same thing. However, there are some subtle syntactic and typechecking differences between them. 234 | 235 | For example, type members may be made non-abstract by "implementing" them with an inner class or trait: 236 | 237 | ```scala 238 | trait CustomStack extends MyStack { 239 | class Elem // an inner class becomes a concretization of abstract type member 240 | } 241 | ``` 242 | 243 | You cannot do something like this with a type parameter. 244 | 245 | On the other hand, type members cannot be used in constructor parameters. Something like this: 246 | 247 | ```scala 248 | class Box[T](val value: T) 249 | ``` 250 | 251 | cannot be (easily) expressed using a type member. 252 | 253 | Using plain generics it is also possible to refer to types like `MyStack[String]` which doesn't seem doable with type members. 254 | 255 | However, it actually *is* doable - a type-member equivalent of `MyStack[String]` would be `MyStack { type Elem = String }`. This syntax is called a *type refinement* - brackets here don't mean a block of code or a class/trait body but have a completely new meaning (a refinement). We don't need to go much into details about this. The trick with refining a type member should be enough for now. 256 | 257 | Actually, you can even define a type alias which will "translate" a type member into type parameter: 258 | 259 | ```scala 260 | type MyStackAux[E] = MyStack { type Elem = E } 261 | ``` 262 | 263 | It's also possible to create a specialized subclass or subtrait which "translates" the type member to type parameter: 264 | 265 | ```scala 266 | trait MyGenericStack[E] extends MyStack { 267 | type Elem = E 268 | } 269 | ``` 270 | 271 | Of course, in this case the "translation" works only inside the hierarchy of that specialized trait, while type alias encompasses every possible subtype of `MyStack`. 272 | 273 | #### Referring to type member from outside 274 | 275 | Using plain generics, you can write e.g. `MyStack[_]` (a stack of unknown element type). An equivalent version of that using type members would simply be `MyStack`. We haven't used a *type refinement* to bind the `Elem` member to some concrete type and so the type member remains abstract. 276 | 277 | However, the difference is not just cosmetic. With generics and wildcards, there is no way to refer to the unknown element type. This is however possible with type members. For example: 278 | 279 | The following code will not compile: 280 | 281 | ```scala 282 | trait MyStack[E] { 283 | def push(e: E): Unit 284 | def pop(): E 285 | } 286 | object Test { 287 | def popAndPush(stack: MyStack[_]): Unit = { 288 | val elem = stack.pop() 289 | stack.push(elem) // error! 290 | } 291 | } 292 | ``` 293 | 294 | The compiler cannot follow the fact that the type of `elem` returned from `pop` is exactly the same type that `push` accepts. The type of `elem` is pretty much `Any`. Using type members, this problem disappears: 295 | 296 | ```scala 297 | trait MyStack { 298 | type Elem 299 | def push(e: Elem): Unit 300 | def pop(): Elem 301 | } 302 | object Test { 303 | def popAndPush(stack: MyStack): Unit = { 304 | val elem = stack.pop() 305 | stack.push(elem) // no problem! 306 | } 307 | } 308 | ``` 309 | 310 | Now the compiler is able to track the types properly. This is because type members (even though they're abstract) can be referred to outside of its containing class/trait. In the above example, `elem` has type `stack.Elem`. If we wanted, we could write it explicitly: 311 | 312 | ```scala 313 | val elem: stack.Elem = stack.pop() 314 | ``` 315 | 316 | `stack.Elem` is an example of *path dependent type*. You may remember this term from discussion about inner classes and traits. Exactly the same rules as these discussed there apply to abstract type members, e.g. if `stack1` and `stack2` are different stack instances, then `stack1.Elem` and `stack2.Elem` are types incompatible with each other (although they still have a common supertype, `Stack#Elem`). 317 | 318 | Of course, there is also an easy way to fix our example and still using plain generics - simply make the `popAndPush` method generic: 319 | 320 | ```scala 321 | trait MyStack[E] { 322 | def push(e: E): Unit 323 | def pop(): E 324 | } 325 | object Test { 326 | def popAndPush[E](stack: MyStack[E]): Unit = { 327 | val elem = stack.pop() 328 | stack.push(elem) 329 | } 330 | } 331 | ``` 332 | 333 | However, adding that generic is somewhat a boilerplate. It isn't very painful in our simple example, but it may become much more annoying when there is e.g. more than just one type parameter, some of the params have type bounds that need to be repeated and you have many places in your code that needs to be made generic just because of that. That's why type members are often much more flexible in such situations. 334 | 335 | #### Naming conventions 336 | 337 | You are probably used to giving one-letter names to type parameters in Java. This convention generally persists also for generics in Scala, but you should **NOT** carry it over to abstract type members. 338 | 339 | Names of type members should be longer and much more descriptive than names of type parameters. This is because names of type members are actually part of the public API, while names of type parameters are not so much. This has two important consequences: 340 | 341 | * Changing the name of type member can introduce a serious backwards compatibility breakage 342 | Let's assume we have a library with a stack trait similar to the one from previous examples: 343 | ```scala 344 | trait Stack { 345 | type Elem 346 | } 347 | ``` 348 | Somebody uses our library and implements the stack: 349 | ```scala 350 | class IntStack extends Stack { 351 | type Elem = Int 352 | } 353 | ``` 354 | If we now change the `Elem` to something else, the implementor's code would be broken. The `type Elem = Int` will still compile but will just become a type alias unrelated to base trait and the type member from `Stack` will remain abstract. 355 | 356 | Note that such problem doesn't exist with plain generics: 357 | ```scala 358 | trait Stack[E] 359 | ``` 360 | Implementation: 361 | ```scala 362 | class IntStack extends Stack[Int] 363 | ``` 364 | Changing `E` to some other name would not break anything. 365 | 366 | * There may be name conflicts between type members inherited from different traits 367 | **TODO** 368 | 369 | #### The my-type problem 370 | 371 | Here's a very common problem in Java, Scala and possibly other object oriented languages with inheritance: how to declare a method that returns "self" type? In other words, how to return the same type as the actual type of an instance on which the method is called? For example: 372 | 373 | ```scala 374 | trait HasName { 375 | def name: String 376 | def withName(newName: String): 377 | } 378 | final class Person(val name: String, val surname: String) extends HasName { 379 | def withName(newName: String): Person = new Person(newName) 380 | } 381 | ``` 382 | 383 | Now, we'd like to replace `` with something that would allow us to write, e.g.: 384 | 385 | ```scala 386 | def renameToJohn[T <: HasName](hasName: T): T = 387 | hasName.withName("John") 388 | ``` 389 | 390 | What should we declare as return type of `withName` so that `renameToJohn` compiles? This is a tricky problem that has a lot of alternative solutions. One of these solutions (a fairly nice one) uses type members and that's why we're dealing with it in this chapter. However, let's go through some more basic solutions first before getting to the point. 391 | 392 | ##### Using `this.type` 393 | 394 | From the discussion about inner classes and path dependent types, you may remember something called *singleton types* - types that belong to `object`s, `val`s and `lazy val`s and represent exactly the single instance that lies underneath. 395 | 396 | One special example of a singleton type is `this.type`. It's a type that represents the `this` instance inside a class or trait. `this.type` is a simple and nice solution to "my-type" problem, assuming that we always return the same instance. For example, `this.type` is particularly good as a return type of "fluent setter" - a method which mutates some internal state of an object and then simply returns it. For example: 397 | 398 | ```scala 399 | trait MutableHasName { 400 | var name: String 401 | def fluentSetName(newName: String): this.type = { 402 | name = newName 403 | this 404 | } 405 | } 406 | ``` 407 | 408 | We can then do: 409 | 410 | ```scala 411 | class MutablePerson extends MutableHasName 412 | object Test { 413 | val person: MutablePerson = new MutablePerson().fluentSetName("John") 414 | } 415 | ``` 416 | 417 | Note that the type of `person` is `MutablePerson` (the concrete class type) even though `fluentSetName` method is defined in `MutableHasName` trait. 418 | 419 | This solution is easy and nice but limited - it can't be used if we want to return something else than exactly the same instance. In our initial example, we return a copy of `Person` which is not a correct value of `this.type`. 420 | 421 | ##### F-bounded polymorphism 422 | 423 | This scary name refers to a fairly simple technique often used e.g. in Java to deal with this-type problem. 424 | 425 | Let's go back to our original example with `HasName` trait and `Person` class. We can introduce a type parameter that will track the "my-type" information: 426 | 427 | ```scala 428 | trait HasName[Self <: HasName[Self]] { 429 | def name: String 430 | def withName(newName: String): Self 431 | } 432 | final class Person(val name: String, val surname: String) extends HasName[Person] { 433 | def withName(newName: String): Person = new Person(newName) 434 | } 435 | ``` 436 | 437 | The recursive `Self <: HasName[Self]` bound is not strictly necessary, but it explicitly ensures that `Self` inherits from `HasName`. This is how F-bounded polymorphism is usually implemented in Java. However, this version has a problem: it does not strictly ensure that `Self` is _always_ set to the implementing class type. For example, we could write: 438 | 439 | ```scala 440 | final class Item(val name: String) extends HasName[Person] 441 | ``` 442 | 443 | Something like this could be a very common mistake stemming from copy-pasting. The compiler does not protect us from this - from its point of view this is perfectly correct code. Because of that the compiler also can't assume that `this` instance is always an instance of `Self` and will reject code like this: 444 | 445 | ```scala 446 | trait SelfTyped[Self <: SelfTyped[Self]] { 447 | def returnSelf: Self = this // ERROR, the compiler can't be sure that `this` is an instance of `Self` 448 | } 449 | ``` 450 | 451 | One way to solve this problem in Scala is to use additional self-type annotation: 452 | 453 | ```scala 454 | trait SelfTyped[Self <: SelfTyped[Self]] { this: Self => 455 | def returnSelf: Self = this 456 | } 457 | ``` 458 | 459 | In general, the solution with type parameter (generic) is nice, but introduces some syntactic crust. We can no longer simply use `HasName` as a standalone type, now we have to always write `HasName[_]` which may be annoying. 460 | 461 | ##### Type member solution 462 | 463 | Let's replace type parameter with type member: 464 | 465 | ```scala 466 | trait HasName { 467 | type Self >: this.type <: HasName 468 | def name: String 469 | def withName(newName: String): Self 470 | } 471 | final class Person(val name: String, val surname: String) extends HasName { 472 | type Self = Person 473 | def withName(newName: String): Person = new Person(newName) 474 | } 475 | ``` 476 | 477 | The `>: this.type` lower bound serves the same purpose as `this: Self =>` self-type annotation from type parameter based solution. Note that lower bound must be specified before upper bound. 478 | 479 | This solves the problem in pretty much the same way the type parameter did, but relieves us from polluting definition of `HasName` with unnecessary generic crust. We don't have to rewrite all usages of `HasName` to introduce a generic or wildcard. 480 | 481 | #### Java interoperability 482 | 483 | Type members are purely Scala feature - Java does not see them. If you want your types to be fully visible from Java code, you must use plain generics instead of type members. 484 | 485 | #### Type members - summary 486 | 487 | Advantages of type members over generics: 488 | * type members don't pollute type declarations of your trait, like generics do 489 | * adding a type member to class or trait is by itself a fully backwards compatible change, while adding a type parameter will require you to rewrite a lot of code that uses your trait or class 490 | * type member may be referred to outside of its declaring class or trait using path-dependent types 491 | * you can concretize an abstract type member with inner class or trait 492 | 493 | Advantages of generics over type members: 494 | * generics may be referred to in constructor parameters (and self-type annotation) 495 | * type parameters are recognized by Java 496 | * names of type parameters may be much more freely changed than names of type members (without breaking backwards compatibility) 497 | 498 | In general, a type member seems to be a good replacement for type parameter in situations where the generic was only there to be made concrete by various subclasses. 499 | 500 | As we have already noted, type members and type parameters are fairly orthogonal concepts that often serve similar purposes. In the possible future version of Scala called [dotty](https://github.com/lampepfl/dotty), type members and type parameters have been merged into a single being. -------------------------------------------------------------------------------- /chapters/Handling-data-in-functional-way.md: -------------------------------------------------------------------------------- 1 | # Handling data in functional way 2 | 3 | In this chapter we're going to explore some basic ways of processing data in a functional way. Most of what we are going to show here has some orthogonal counterpart in object oriented programming. Scala tries to blend functional and object oriented programming in a single language but it is not necessarily a good idea to mix these two styles. That's why we're going to try to provide some guidelines on when to use functional and object oriented approach and make the distinction as clear as possible. 4 | 5 | ## Some principles of functional programming 6 | 7 | ### Immutability 8 | 9 | Functional programming is a lot about ability to reason abstractly about programs. Therefore, in functional design, we like to put as much constraints on our data and code as possible while still retaining its ability to do what we want. 10 | 11 | One of such essential constraints that functional programming leverages is **immutability**. When we're sure that our data structures are immutable, we can relieve ourselves from much pain of debugging and tracking possibly very unpredictable mutations in our program. Code that uses only immutable data is much easier to refactor, test, parallelize and sometimes even optimize for performance. 12 | 13 | *Ok, but if all data is immutable then how do we, for example, update some map with a key-value pair?* 14 | 15 | When working in immutable data structures, "modifying" does not mutate the data in place but rather creates completely new piece of data based on the original data. For example, putting a key-value pair to an immutable map actually creates a new map based on the original map with one more entry added to it. 16 | 17 | *Ok, but doesn't this require a lot of copying and is bad for performance?* 18 | 19 | Immutable data structures are indeed usually slower than mutable ones, but it's not as bad as it may seem. Implementations of immutable data structures use some very clever tricks to share as much data between consecutive instances as possible. This way they mostly avoid copying. For example, updating an immutable map in Scala with a key-value pair is still a nearly constant-time operation. Such data structures are often called *persistent data structures*. We'll talk more about immutable collections in a separate chapter. For now we just want to outline a general value of immutability. 20 | 21 | Despite the fact that working with immutable data usually seems to be slower than with mutable data, there are often situations where it's the mutability that hurts performance. 22 | 23 | Java is *very* bad at immutability. Working with immutable data in Java requires so much boilerplate that usually we just give up and introduce some mutable state. In particular, all standard Java collections are mutable. Or rather should we say - they can never be assumed to be immutable. The example below shows how mutability may hurt: 24 | 25 | ```java 26 | public class Tweet { 27 | private List hashTags; 28 | 29 | public void setHashTags(List hashTags) { 30 | this.hashTags = new ArrayList<>(hashTags); 31 | } 32 | } 33 | ``` 34 | 35 | As you can see, the setter always makes a defensive copy of the list it accepts as the argument. If it wants to be really safe, it really must do it, because we cannot be sure if some other code has access to this list and may mutate it outside of our control. This is a good example where it's the mutability that forces us to copy a lot of data. If the list was immutable, we wouldn't need it. 36 | 37 | **Immutable data can be safely shared.** 38 | 39 | That property is especially useful when sharing between multiple threads. This relieves us from much pain stemming from handling concurrent changes of mutable state and tricky synchronization issues that come with it. 40 | 41 | ### Separation of code and data 42 | 43 | Apart from immutability of data, you'll also see that in functional design, there is usually a very clear separation of code and data. Definitions of data types and functions that work on them are completely separate. You could say that in functional languages, the API of data types lies "outside" of them and can be different in various contexts. 44 | 45 | Note that this is quite the opposite of what object-oriented programming is proposing. When we create a class, put some fields in it and some methods that work on these fields, we strongly tangle some data with some code. 46 | 47 | We're not saying that any of the approaches is better than the another - each has its own advantages. Functional approach provides very clear separation of concerns while the object-oriented approach gives us nice encapsulation of data. However, Scala has the advantage over Java here because it allows us to use *both* ways while Java pretty much only supports the object-oriented approach. Of course, with great power comes great responsibility - having two significantly different methods available for us within a single language, we must be very careful not to create a lot of mess by mixing these approaches. 48 | 49 | ## Basic immutability and code-data separation in Scala 50 | 51 | ### Tail recursion 52 | 53 | We say that a recursive function is *tail recursive* when the recursive call is the *very last* thing which that function does. Here's an example: 54 | 55 | ```scala 56 | def printElementsFrom(arr: Array[String], idx: Int): Unit = { 57 | val len = arr.length 58 | if(idx < length) { 59 | println(arr(idx)) 60 | printElementsFrom(arr, idx + 1) 61 | } 62 | } 63 | ``` 64 | 65 | The function above prints elements of an array starting at index `idx`. In particular, we could use it to print all elements of an array by calling it with `idx == 0`. Note how the recursive invocation is the last thing the function does. When the recursive invocation finishes, its "parent" invocation has nothing more to do. 66 | 67 | Scala is able to use this nice property to perform the so-called *tail call optimization*. In standard recursion, every recursive call of the function has its own local variables and other data on the program stack. If the recursion is very deep, we may get a `StackOverflowError` or at least use a lot of memory. 68 | 69 | But with tail recursion, we can avoid this. Recursive call is the last thing the function does, so it no longer needs its local variables at that point. This way the recursive call can reuse stack space allocated by its "parent" invocation for its own local variables and prevent the stack from growing. 70 | 71 | Note that this effectively makes our function work just like a loop - it simply iterates and prints every element of the array - all in small, constant amount of memory used. If you look into the bytecode, you'll see that the tail-recursive function is compiled to the same bytecode as plain `while` loop. 72 | 73 | However, a `while` loop would require mutable variable. We don't have any of these in our tail-recursive function. We've just written a loop that works on completely immutable data and is as fast as a `while` loop! 74 | Note also that this can be faster than using `arr.foreach(println)` - the `foreach` requires an anonymous function to be created which introduces runtime overhead. 75 | 76 | As you can see, tail recursion is the most basic tool to retain immutability of data in our code. It can also be much more readable than plain old variable-mutating imperative code. We would actually recommend tail recursive function as a default option when having to write a loop. This is fairly easy also thanks to the fact that Scala has local functions. 77 | 78 | #### The `@tailrec` annotation 79 | 80 | Scala has a nice annotation that can help you avoid accidentally writing non-tail-recursive functions where you intended to use tail recursion. Simply annotate your method with `@tailrec` and if the compiler detects that your method is not tail-recursive, it will give you an error. 81 | 82 | #### Indirect tail recursion and trampolining 83 | 84 | **TODO** 85 | 86 | ### Immutable records - case classes 87 | 88 | The most basic piece of compound data is a *record* - something that takes a bunch of values of various types, gives each of them a name and wraps them together into a single object. A record that represents a postal address (say city, zipcode, street name and building number) is a very simple example. 89 | 90 | Of course, we care about immutability so our records will also be immutable - if we want to modify one of the fields inside a record, we actually create a new record that is a copy of the old one but with this single field changed. Also, it is completely natural to compare records for equality or use them as keys in maps. We would also expect them to have a nice string representation if there's a need to print them during e.g. debugging. 91 | 92 | Java is absolutely *terrible* at defining immutable records. It requires a ridiculous amount of boilerplate to meet all the expectations listed above. For example, in order to create a two-field immutable record that holds name and surname, we would need to write a serious amount of code: 93 | 94 | ```java 95 | import java.util.Objects; 96 | 97 | public final class Person { 98 | private final String name; 99 | private final String surname; 100 | 101 | public Person(String name, String surname) { 102 | this.name = name; 103 | this.surname = surname; 104 | } 105 | 106 | public String getName() { 107 | return name; 108 | } 109 | 110 | public String getSurname() { 111 | return surname; 112 | } 113 | 114 | public Person withName(String newName) { 115 | return new Person(newName, surname); 116 | } 117 | 118 | public Person withSurname(String newSurname) { 119 | return new Person(name, newSurname); 120 | } 121 | 122 | @Override 123 | public boolean equals(Object other) { 124 | if(other instanceof Person) { 125 | Person otherPerson = (Person)other; 126 | return Objects.equals(name, otherPerson.name) && 127 | Objects.equals(surname, otherPerson.surname); 128 | } else { 129 | return false; 130 | } 131 | } 132 | 133 | @Override 134 | public int hashCode() { 135 | return Objects.hash(name, surname); 136 | } 137 | 138 | @Override 139 | public String toString() { 140 | return "Person(" + name + ", " + surname + ")"; 141 | } 142 | } 143 | ``` 144 | 145 | This is a horrendous amount of boilerplate code. Here's a Scala equivalent: 146 | 147 | ```scala 148 | case class Person(name: String, surname: String) 149 | ``` 150 | 151 | That's it. One line and we meet all the requirements stated earlier. That single line also provides some more features which Java simply has no equivalent of. We'll talk about them later. For now, let's explain what exactly is a *case* class. 152 | 153 | Declaring some class as `case class` causes the following to happen: 154 | * Companion object is automatically created for the class. It contains autogenerated `apply` method which allows us to create instances without the `new` keyword: 155 | 156 | ```scala 157 | val person = Person("Fred", "Kowalski") 158 | ``` 159 | 160 | Actually, every companion object of a case class automatically extends one of the `Function` traits, depending on the number of fields. Our `Person` class has two fields of type `String` and so the `Person` object extends `Function2[String,String,Person]`, i.e. `(String, String) => Person`. 161 | 162 | * Fields of the case class are automatically visible as public members: 163 | 164 | ```scala 165 | val person = Person("Fred", "Kowalski") 166 | println(s"Hi, ${person.name}!") 167 | ``` 168 | 169 | * The class has automatically generated `equals`, `hashCode` and `toString`. 170 | * The class also has automatically generated `copy` method which allows us to make copies of instances with some of the fields changed: 171 | 172 | ```scala 173 | val person = Person("Fred", "Kowalski") 174 | val otherPerson = person.copy(surname = "Nowak") 175 | ``` 176 | 177 | The `copy` method is especially useful when the class has a lot of fields. 178 | 179 | * We can *pattern-match* against the class. We will soon explain in detail what that means. For the sake of completeness, we'll mention that this is allowed thanks to the automatically generated magic `unapply` method in the companion object. Don't worry about what that means now, though. 180 | * Every case class also extends one of the Scala `Product` traits (`Product1` to `Product22`). This causes the class to have the `productArity`, `productElement`, `productIterator` and `productPrefix` methods. However, these are not very interesting, so we won't be elaborating on them. 181 | 182 | ### Pattern matching 183 | 184 | Pattern matching is one of the flagship features of every functional programming language and one of the most powerful features of Scala. 185 | 186 | Pattern matching is closely related to case classes. In previous section, we mentioned that we can pattern match *against* them. Now we're going to show what that means. 187 | 188 | Pattern matching essentially allows us to check if some value has a specific type and shape (it "matches a particular pattern") and deconstruct that value according to that pattern at the same time (with concise syntax). Let's define a slightly more complex case class for personal data: 189 | 190 | ```scala 191 | case class Address(city: String, zipcode: String, street: String, number: Int) 192 | case class Person(name: String, surname: String, address: Address) 193 | ``` 194 | 195 | In the simplest form of pattern matching, we simply deconstruct an instance of `Person` by giving its parts some identifiers and doing something with them. 196 | 197 | ```scala 198 | val person = Person(...) 199 | person match { 200 | case Person(name, surname, address) => 201 | println(s"Hello, $name $surname!") 202 | } 203 | ``` 204 | 205 | In this case, we use fields of the `person` object to print a message. But as everything else in Scala, a pattern match is an expression that evaluates to some value. The above snippet could be rewritten as: 206 | 207 | ```scala 208 | val message = person match { 209 | case Person(name, surname, address) => 210 | s"Hello, $name, $surname" 211 | } 212 | println(message) 213 | ``` 214 | 215 | We have given the parts of `person` object identifiers `name`, `surname` and `address` - exactly the same names as in the definition of `Person` case class. This is natural in this case, but it's not required. We could have used any names we liked. 216 | 217 | ##### Wildcard patterns 218 | 219 | You may have also noticed that we didn't use the `address` in our pattern match. In such case, we may completely omit the name for an unused value and replace it with *wildcard*: 220 | 221 | ```scala 222 | val message = person match { 223 | case Person(name, surname, _) => 224 | s"Hello, $name $surname" 225 | } 226 | ``` 227 | 228 | ##### Matching literal values 229 | 230 | The above pattern match does not actually test any type or structure - we know that `person` is a `Person` so it cannot have different type or structure that our pattern expects. In other words, it's impossible for the above pattern match to fail. 231 | 232 | But that's of course not always the case. We can require our `person` object to have more specific structure. For instance, we may require that the `name` is *exactly* "John" - by using a fixed literal value instead of an identifier that would match anything: 233 | 234 | ```scala 235 | val message = person match { 236 | case Person("John", _, _) => 237 | s"Hi, Johnny!" 238 | } 239 | ``` 240 | 241 | We may also extract our literal to a constant: 242 | 243 | ```scala 244 | val TheName = "John" 245 | val message = person match { 246 | case Person(TheName, _, _) => 247 | s"Hi, Johnny!" 248 | } 249 | ``` 250 | 251 | However, be careful with this! There's a peculiar syntactic rule hidden in there. If we changed the name of our constant to `theName` (lowercase), it will no longer work. Instead of matching exactly the value inside `theName` constant, it would match *any* value and simply give it *identifier* `theName`. Such identifier would just shadow the constant that we defined: 252 | 253 | ```scala 254 | val theName = "John" 255 | val message = person match { 256 | // this will match anything, not just "John" 257 | // theName in the pattern is a completely new identifier which shadows the one outside 258 | case Person(theName, _, _) => 259 | s"Hi, Johnny!" 260 | } 261 | ``` 262 | 263 | Scala decides what to do solely based on whether the identifier starts with uppercase or lowercase letter. This may be very confusing if you're not aware of that detail. 264 | 265 | It is generally recommended to use uppercase-starting names for external constants. However, as a last resort, you can put the name of your constant inside backticks. This will force the compiler back into treating the identifier as reference to a constant: 266 | 267 | ```scala 268 | val theName = "John" 269 | val message = person match { 270 | // this will match only "John" thanks to backticks 271 | case Person(`theName`, _, _) => 272 | s"Hi, Johnny!" 273 | } 274 | ``` 275 | 276 | ##### When matching fails 277 | 278 | Looking at previous example - what will happen if the name isn't "John"? In such case the pattern match will fail by throwing a `MatchError` (which, contrary to its name, is actually a `RuntimeException`). That's not something we're happy about. What we'd want is to do something special when the name is "John", but do something more generic when it isn't. We can easily achieve this by combining two previous patterns: 279 | 280 | ```scala 281 | val message = person match { 282 | case Person("John", _, _) => 283 | s"Hi, Johnny!" 284 | case Person(name, surname, _) => 285 | s"Hello, $name $surname!" 286 | } 287 | ``` 288 | 289 | When we have multiple cases in the pattern match, Scala will check one after another until it finds the first that matches. All other ones will be ignored, even if some of them would match, too. In other words, the order of cases in pattern matching is important. We generally put more specific cases above the more generic ones. The `MatchError` will be thrown only when none of the cases matches. 290 | 291 | Note that this also means that we don't need to put any `break` statements at the end of each case, like we do in the `switch` statement in C or Java. Scala doesn't even have the `break` statement. Unlike in `switch` statement, there are also no problems with declaring local variables inside pattern matching cases. Each case body has its own scope. 292 | 293 | ##### Empty case bodies 294 | 295 | If you recall the first chapter of this tutorial, you'll remember that there was a confusing pitfall with the `if` statements which don't have the `else` clause. The Scala compiler silently infers `else ()` in such situations, which may cause very confusing compilation errors. 296 | 297 | Pattern matching has a similar pitfall - if you forget to implement the body of any of the cases by putting nothing after the `=>` sign, the compiler will silently insert `()` in there and you may run into exactly the same problems as with the `if` statement (e.g. the type of pattern match may be confusingly inferred as `Any`). 298 | 299 | ##### Alternatives 300 | 301 | Imagine we want to handle "Johnny" in the same way as "John". In the most straightforward way, we would simply add one more case to the pattern match to do this: 302 | 303 | ```scala 304 | val message = person match { 305 | case Person("John", _, _) => 306 | s"Hi, Johnny!" 307 | case Person("Johnny", _, _) => 308 | s"Hi, Johnny!" 309 | case Person(name, surname, _) => 310 | s"Hello, $name $surname!" 311 | } 312 | ``` 313 | 314 | However, we are repeating ourselves this way - we have two cases with the same body, which is not nice. Fortunately, we can use the `|` operator to combine two patterns into one - an alternative of its arguments: 315 | 316 | ```scala 317 | val message = person match { 318 | case Person("John", _, _) | Person("Johnny", _, _) => 319 | s"Hi, Johnny!" 320 | case Person(name, surname, _) => 321 | s"Hello, $name $surname!" 322 | } 323 | ``` 324 | 325 | Unfortunately, the above will not work if we wanted to give an identifier to the person's surname or address. For instance, this will fail with compilation error: 326 | 327 | ```scala 328 | val message = person match { 329 | // error! 330 | case Person("John", surname, _) | Person("Johnny", surname, _) => 331 | s"Hi, Johnny $surname!" 332 | case Person(name, surname, _) => 333 | s"Hello, $name $surname!" 334 | } 335 | ``` 336 | 337 | However, this example is simple enough so that we can redeem ourselves a bit. Notice that the two patterns that we combined with the `|` operator differ only by the name literal. We can use this to simplify our pattern: 338 | 339 | ```scala 340 | val message = person match { 341 | case Person("John" | "Johnny", surname, _) => 342 | s"Hi, Johnny $surname!" 343 | case Person(name, surname, _) => 344 | s"Hello, $name $surname!" 345 | } 346 | ``` 347 | 348 | The `|` operator can be used anywhere in the pattern. 349 | 350 | ##### Nesting patterns 351 | 352 | We have deconstructed an instance of `Person` case class, but it also has another case class as one of its fields - the `Address`. So far, we ignored it, but what if we wanted to deconstruct it, too? We can do this with no problem - patterns can be arbitrarily nested and combined: 353 | 354 | ```scala 355 | val message = person match { 356 | case Person(name, surname, Address(city, _, _, _)) => 357 | s"Hello, $name $surname from $city!" 358 | } 359 | ``` 360 | 361 | ##### Bind operator 362 | 363 | In above example, deconstruction of `Address` gives us nice syntax to access `city`. But what if we wanted to access both the `city` and the address itself? There's no identifier assigned to an address. Fortunately, we can give it one without destroying the pattern. To do that, we use the *bind* operator: 364 | 365 | ```scala 366 | val message = person match { 367 | // we have identifiers for both the address and the city 368 | case Person(name, surname, addr@Address(city, _, _, _)) => 369 | val formattedAddress = formatAddress(addr) 370 | s"Hello, $name $surname from $city! It seems that you live at $formattedAddress." 371 | } 372 | ``` 373 | 374 | Bind operator can be used with any pattern on its right side. For example, it could be an alternative of patterns to which we give a common name. 375 | 376 | ##### `instanceof`-like patterns 377 | 378 | So far our patterns only worked with case classes (to check for particular structure) or literal values (to check for equality). However, pattern matching can be used to perform arbitrary runtime type checking. This is similar to Java's `instanceof` operator. 379 | 380 | For example, let's assume we just deserialized some object that came from a network and we don't know its type. We just know that it may be an `Int` or `String` or a `Boolean`. Pattern matching allows us to perform runtime tests for these types in concise syntax: 381 | 382 | ```scala 383 | def handleInt(i: Int) = ... 384 | def handleString(s: String) = ... 385 | def handleBoolean(b: Boolean) = ... 386 | 387 | val any: Any = getFromNetwork() 388 | any match { 389 | case i: Int => handleInt(i) 390 | case s: String => handleString(s) 391 | case b: Boolean => handleBoolean(b) 392 | case _ => throw new Exception("Can't handle this value!") 393 | } 394 | ``` 395 | 396 | Notice how each case does two things at once - it first checks whether matched value has particular type and if it has, it gives it a new identifier which is already aware of that type. This is something you would usually do in two separate steps in Java - first you would test the type with `instanceof` operator and then cast the value to that type. 397 | 398 | The example above shows dedicated syntax (similar to type ascription) for `instanceof`-like patterns. However, runtime type check is also "hidden" inside every other pattern match. For example, nothing stops us from matching our completely untyped `any` value against a case class: 399 | 400 | ```scala 401 | def handlePerson(name: String, surname: String) = ... 402 | 403 | val any: Any = getFromNetwork() 404 | any match { 405 | case Person(name, surname, _) => handlePerson(name, surname) 406 | ... 407 | } 408 | ``` 409 | 410 | In this example, Scala will first test whether `any` is actually a `Person` and only then it will proceed to deconstruct it against the pattern. 411 | 412 | **WARNING** Pattern matching used to perform runtime type checks is probably the easiest way to abuse its power. It gives us a nice syntax for doing an `instanceof` and type cast at the same time, but it should be avoided just as usage of `instanceof` is avoided in Java. Runtime type checks ("typecasing") are an enemy of type safety and nice abstractions. They go against ability to reason abstractly about code and thus make it much harder to change and maintain. Sometimes they're unavoidable (like when deserializing stuff from network), but having too much of them usually indicates bad design. 413 | 414 | Runtime type checking with pattern matching is also limited in the same way as `instanceof` checks are limited in Java by the type erasure. That means you can't check e.g. if your object is a `List[String]` - you can only check if it's a `List[_]` (list of anything). Only the *class* information is retained in runtime, not the full type. 415 | 416 | ##### Guards 417 | 418 | Every pattern matching case may be *guarded* by an arbitrary logical expression which can access deconstructed values. Here's how to do it: 419 | 420 | ```scala 421 | val message = person match { 422 | case Person(name, surname, _) if name == surname => 423 | s"Why did your parents give you the same name as your surname, $name?" 424 | } 425 | ``` 426 | 427 | ### Partial functions 428 | 429 | Having a function `A => B` we usually assume that the function is safe to call on any argument of type `A` (or at least we would like to). However, this is not always the case - the function may throw an exception. For example: 430 | 431 | ```scala 432 | val fun: String => Int = 433 | _.toInt 434 | ``` 435 | 436 | Calling `fun("abc")` will throw a `NumberFormatException`. We clearly see that the function is properly defined only for some of the possible values that can be technically passed to it. In this case we can pass any string value to `fun` but it will only return result for these strings which are correct representations of integer numbers. 437 | 438 | Scala has a specialized function type which allows us to ask whether it is defined for given argument or not: `PartialFunction`. 439 | 440 | `PartialFunction` inherits `Function1`, so it has a regular `apply` method, but it also has the `isDefinedAt` operation which allows us to ask whether that partial function is defined for particular value or not. 441 | 442 | `PartialFunction` also exposes some convenience methods: 443 | 444 | * `applyOrElse` - this is like `apply` but allows us to provide a fallback function that will be used if the partial function is not defined for particular value. 445 | * `orElse` - combines two partial functions together into a single partial function. The second partial function serves as a fallback which is used when the first function is not defined for some value. 446 | * `lift` - transforms a `PartialFunction[A,B]` into a `Function[A,Option[B]]`. An `Option[B]` may be one of two things - either `Some[B]` which contains the `B` value returned by original partial function or `None` if the partial function was not defined for particular value. We'll talk about the `Option` later in more detail. 447 | 448 | Scala also provides a dedicated syntax for defining `PartialFunction`s. It's based on the pattern matching syntax. Here's how we would redefine our string-to-integer parsing function as a partial function: 449 | 450 | ```scala 451 | val fun: PartialFunction[String,Int] = { 452 | // let's ignore negative numbers for simplicity 453 | case str if str.forall(_.isDigit) => 454 | str.toInt 455 | } 456 | ``` 457 | 458 | As you can see, the partial function body is like a pattern match, but without the ` match` part at the beginning. You can think of it as if the argument of partial function was pattern-matched against our list of `case` definitions. If none of the cases match, the partial function will say that it's not defined for that argument. 459 | 460 | Now, if we want to use `0` as a default value for the result (in case the string is not a proper integer representation), we could do it like this: 461 | 462 | ```scala 463 | val string: String = ... 464 | val parsed: Int = fun.applyOrElse(string, _ => 0) 465 | ``` 466 | 467 | Note that `PartialFunction` is still a `Function` so you can use the partial function syntax when using various higher-order functions. For example: 468 | 469 | ```scala 470 | case class Person(name: String, surname: String) 471 | val listOfPersons: List[Person] = ... 472 | val surnamesOfJohnnyOrEmpty = listOfPersons.map { 473 | case Person("Johnny" | "John", surname) => surname 474 | case _ => "" 475 | } 476 | ``` 477 | 478 | However, be careful - the `map` method that we have used is not aware that its argument is a `PartialFunction` and it's unable to check whether it is defined or not for particular argument. Therefore, if you pass partial functions where regular functions are expected, make sure that the PF is *always* defined. This way it's not going to be "partial" anymore, but the point is that we can still use the nice pattern matching like syntax. 479 | 480 | #### Partial function syntax for multi argument functions 481 | 482 | Sometimes it's also worth to know that Scala will also accept partial function syntax where multi-argument functions are expected. For example: 483 | 484 | ```scala 485 | def takeFunctionTwo(fun: (Int,String) => String) = ??? 486 | takeFunctionTwo { 487 | case (int, string) => /* do something */ 488 | } 489 | ``` 490 | 491 | ### Pattern assignment 492 | 493 | Let's look at this simple code: 494 | 495 | ```scala 496 | val x = someVeryLongExpression() 497 | println(x) 498 | ``` 499 | 500 | Although there's no real point in writing it like this, technically this code does the same thing as: 501 | 502 | ```scala 503 | someVeryLongExpression() match { 504 | case x => println(x) 505 | } 506 | ``` 507 | 508 | Now, let's assume the pattern match from above is a little more complicated, e.g. 509 | 510 | ```scala 511 | somePerson() match { 512 | case Person(name, surname) => println(s"Hi, $name $surname!") 513 | } 514 | ``` 515 | 516 | Now the pattern is not as trivial as before - we extract two values (name and surname) from a case class. However, we can still go back to the previous syntax: 517 | 518 | ```scala 519 | val Person(name, surname) = somePerson() 520 | println(s"Hi, $name $surname!") 521 | ``` 522 | 523 | This is a very handy syntactic feature of Scala. You can use pretty much any pattern on the left side of an assignment. However, you have to be careful - if the value on the right side of assignment does not match the pattern, a `MatchError` will be thrown. 524 | 525 | ### Extractors 526 | 527 | **TODO** 528 | 529 | ### Working with tuples 530 | 531 | **TODO** 532 | 533 | ### Algebraic data types 534 | 535 | **TODO** 536 | 537 | #### Common ADTs from standard library 538 | 539 | **TODO** 540 | 541 | * `List` 542 | * `Option` 543 | * `Either` 544 | * `Try` 545 | 546 | ### For comprehensions 547 | -------------------------------------------------------------------------------- /chapters/Implicits.md: -------------------------------------------------------------------------------- 1 | *Implicits* are an important, unique and controversial Scala feature. They are a low-level language mechanism that underlies many hihger-level features and are responsible for much of Scala's expresiveness, conciseness and type safety. 2 | 3 | Awesome, but what are they, actually? 4 | 5 | ## The `implicit` keyword 6 | 7 | In Scala, every *term member* may be marked as `implicit`. This includes: 8 | * local `val`s, `var`s, `lazy val`s, `def`s and `object`s 9 | * `val`s, `var`s, `lazy val`s, `def`s and `object`s which are members of some class, object or trait 10 | 11 | ```scala 12 | def main(args: Array[String]): Unit = { 13 | implicit val executionContext: ExecutionContext = ??? 14 | implicit def conversion(s: String): ByteString = ByteString(s) 15 | } 16 | trait TypeInfo[T] 17 | object TypeInfo { 18 | implicit object stringInfo extends TypeInfo[String] 19 | } 20 | ``` 21 | 22 | When some symbol is marked as `implicit`, the compiler is allowed to use it "automatically" in some particular contexts. This means that the compiler may automatically insert a reference to an implicit value into the compiled code when it "needs" it. 23 | 24 | ## When does the compiler look for implicits? 25 | 26 | ### Implicit parameters 27 | 28 | The first situation where the compiler may automagically use some implicit symbol is when a method or constructor invocation lacks *implicit parameters*. 29 | 30 | What are implicit parameters? As you already know, every method and constructor in Scala can take more than one parameter list. Additionally, the last parameter list (and only the last) may be marked as `implicit`: 31 | 32 | ```scala 33 | def doSomethingWith[T](value: T)(implicit ec: ExecutionContext, ct: ClassTag[T]) = ??? 34 | ``` 35 | 36 | **NOTE** Although syntactically it looks like the `implicit` keyword applies to the `ec` parameter, it's not true - it applies to the entire parameter list, i.e. both `ec` and `ct` are *implicit parameters*. 37 | 38 | `implicit` keyword applied to a parameter group has two effects: 39 | * when invoking the method, implicit parameters *may be omitted* and the compiler will try to *find* appropriate implicit values and *automatically* pass them to the method. 40 | * implicit parameters themselves are seen inside method body as implicit values (i.e. as if they were local `implicit val`s) 41 | 42 | *Example* - perhaps the most commonly used API in Scala standard library that uses implicit parameters are methods on `Future`s. Most of the `Future` API consists of methods which register a listener that will consume the result of the `Future` when it's complete, e.g. `onComplete`, `foreach`, `map`, `flatMap`, `recover`. That listener cannot be executed just anywhere - it needs to be submitted to an `ExecutionContext` which usually represents some thread pool or task queue. Since we usually invoke many `Future` methods in a small code block, it makes sense to accept the `ExecutionContext` as implicit parameter, so that we don't need to manually pass it to every single invocation. 43 | 44 | ```scala 45 | def getUserIdByName(name: String): Future[Long] = ??? 46 | def getUserAge(id: Long): Future[Int] = ??? 47 | 48 | val name = "Implisław" 49 | 50 | import ExecutionContext.Implicits.global 51 | 52 | getUserIdByName(name) 53 | .flatMap(id => getUserAge(id) 54 | .map(age => s"$name (ID $id) is $age years old") 55 | ) 56 | .foreach(println) 57 | ``` 58 | 59 | Without the implicit parameter this would look like this: 60 | 61 | ```scala 62 | getUserIdByName(name) 63 | .flatMap(id => getUserAge(id)(executionContext) 64 | .map(age => s"$name (ID $id) is $age years old")(executionContext) 65 | ) 66 | .foreach(println)(executionContext) 67 | ``` 68 | 69 | The "implicitness" is especially important here, because the above example can be rewritten to use the for-comprehension syntax: 70 | 71 | ```scala 72 | val name = "Implisław" 73 | for { 74 | id <- getUserIdByName(name) 75 | age <- getUserAge(id) 76 | } { 77 | println(s"$name (ID $id) is $age years old") 78 | } 79 | ``` 80 | 81 | The way for-comprehension syntax is translated into actual method calls is hardwired into the Scala parser and can't be changed. For example, the above code is parsed as: 82 | 83 | ```scala 84 | getUserIdByName(name).flatMap(id => getUserAge(id).foreach(age => println(s"$name (ID $id) is $age years old"))) 85 | ``` 86 | 87 | The parser always generates `flatMap` and `foreach` calls which take exactly one parameter. But using implicit parameters, we can "smuggle" some additional dependencies, like `ExecutionContext` in this case. 88 | 89 | ### Implicit conversions 90 | 91 | The second situation when the compiler may automatically use an implicit value is when some types in the code don't match (i.e. when we pass a value of wrong type to some method). In such case, the compiler will not immediately issue an error, but first it will try to find an *implicit conversion*. 92 | 93 | An implicit conversion is any implicit value whose type is a `Function1`. For example: 94 | 95 | ```scala 96 | implicit val stringToInt: String => Int = _.toInt 97 | ``` 98 | 99 | Here's an example of a situation when the compiler will automatically use the conversion: 100 | 101 | ```scala 102 | implicit val stringToInt: String => Int = _.toInt 103 | def takeInt(i: Int): Unit = ??? 104 | takeInt("123") // this will compile thanks to `stringToInt` being in scope 105 | ``` 106 | 107 | Passing parameters or returning a value are the most obvious situations where types may not match. What may be less apparent is that accessing a nonexistent member or member with wrong signature on some value is treated similarly by the compiler. For example: 108 | 109 | ```scala 110 | "somestring".toByteString 111 | ``` 112 | 113 | Of course, there is no such method as `toByteString` on the `String` type. But the compiler will not immediately issue an error. Before that, it's going to try finding an implicit conversion from `String` to a type that actually has a member named `toByteString`. In other words, it will look for an implicit value of type `String => { def toByteString: Any }`. The `{ def toByteString: Any }` part is so called *structural type* - a type identified not by some class or trait name and parameters but by signatures of its members. 114 | 115 | Implicit conversions used to extend APIs of existing types are usually called *implicit views* and methods that they provide are usually called *extension methods*. 116 | 117 | ## Where does the compiler look for implicits? 118 | 119 | We've just said that the compiler automatically searches for values marked as implicit when it needs them and we specified well *when* it needs them. But where exactly does it look for implicits? 120 | 121 | In examples above, we always defined implicit values so that they're visible in the local scope of code that requires them. We did that either by putting implicit definitions directly into the local scope or by importing them into local scope from somewhere. 122 | 123 | The compiler indeed looks for implicits in the current scope. That means, it may use every implicit symbol which can be referred to just by its name, without any prefix. This includes local symbols, members of all enclosing classes plus their superclasses/supertraits. This also includes all symbols imported into current scope. 124 | 125 | But if the compiler can't find an implicit with proper type, it doesn't stop there. Before failing, it looks into what's called *implicit scope* of the type that it's looking for. 126 | 127 | The name "implicits" by itself doesn't mean anything and encompasses two independent but closely related features *implicit parameters* and *implicit conversions*. You'll also hear about *implicit classes* and *implicit views* which are just other names or special cases of implicit conversions. 128 | 129 | ### Implicit parameters 130 | 131 | **TODO** 132 | 133 | * last parameter list of a method or constructor may be marked as *implicit*, using the `implicit` keyword - this means that if we don't supply these parameters to a method call, the compiler will try to automatically find appropriate values 134 | * only values marked by themselves as `implicit` are eligible to be passed as implicit parameters 135 | * implicit `def`s that take parameters themselves are seen as implicit function values, unless the parameters of the `def` are themselves implicit - this would mean a chained implicit (an implicit which depends on other implicits) 136 | * implicit parameters are themselves seen as implicit values inside the method body 137 | * implicits are searched for in current scope or, when not found, in the *implicit scope* which consists of companion objects of classes and traits that occur in the type of the implicit, or their base classes and traits 138 | * beware of shadowing! 139 | * overloading resolution 140 | * the type of an implicit should be explicit 141 | 142 | Implicit parameter usages: 143 | * static dependency injection, e.g. `ExecutionContext` 144 | * additional type information, e.g. `ClassTag` 145 | * additional type constraints, e.g. `<:<` 146 | * type classes - later 147 | 148 | Implicit conversions: 149 | * as part of "implicit parameters" feature, the compiler looks for implicits when it needs something to pass as an implicit parameter 150 | * as part of "implicit conversions" feature, the compiler also looks for implicit values of type `A => B` when it has value of type `A` in a context where value of type `B` is required 151 | * implicit `def`s are implicit conversions by eta-expansion 152 | * additionally, as part of "implicit conversions" feature, the compiler looks for implicit conversions which would allow it to call some otherwise unresolved member of some type - extensions methods 153 | * `implicit class` syntax, value classes 154 | * extension methods - actually more powerful than regular methods 155 | 156 | Convenience implicit conversions - why not cool: 157 | * we usually want the conversion to work in only one specific context, but it will pollute everything 158 | * alternative: extension methods and "converters" 159 | * extension methods and overloading 160 | * implicit conversion to controlled type - magnet pattern example 161 | * still annoying - implicit conversions don't chain 162 | * refactor method into generic and implicit conversion stops working 163 | 164 | Type classes: 165 | * the idea in Haskell - `Monoid` example 166 | * a way to do polymorphism 167 | * scala encoding using implicits, ops implicit class 168 | * `Ordering`, `Numeric` 169 | * more powerful than inheritance 170 | * we can define typeclass instances for arbitrary types, not classes 171 | * we can define typeclass instances for types not defined by us 172 | * we can express complex rules for type class instances and dependencies between typeclasses 173 | * `Foldable`, higher-kinds and ops -------------------------------------------------------------------------------- /chapters/Macros.md: -------------------------------------------------------------------------------- 1 | # Scala macros 2 | 3 | Macros are a very controversial feature of Scala. They have been introduced to the language back in 2013 in 4 | version 2.10 as an experimental feature because of their tight coupling to the architecture of Scala compiler itself 5 | with many of its low-level technical details, quirks, limitations and accidental complexity. For this reason, they 6 | are rather hard to use because to write macros correctly and safely requires knowledge of some rather non-obvious, 7 | not very well documented internals regarding how Scala programs are represented and processed in the compiler. 8 | 9 | Macros have never left their experimental state and in theory they have never become a fully supported language feature. 10 | However, they turned out to be very powerful and useful and since their inception they have become a basis for many 11 | popular, foundational libraries. [Shapeless](https://github.com/milessabin/shapeless) is an example of far-fetched 12 | exploitation of macros that practially makes _magic_ possible in Scala. For this reason, macros have _de facto_ become 13 | a standard part of the language. 14 | 15 | ## Types of macros 16 | 17 | Macros, in their most general sense are _transformations_ that a programmer can define to be done _during compilation_ 18 | on some parts of the program. In other words, macros are a _metaprogramming_ facility. Macros can also _generate_ code 19 | - not only as a transformation of other pieces of code but also based on compile-time reflection on types defined anywhere 20 | in the program. In practice, this might mean transforming/generating an expression but also an entire class/object definition 21 | or even a type (e.g. one being inferred by the compiler somewhere). 22 | 23 | ## `def` macros 24 | 25 | `def` macros are the most important macro flavor. They are macros which are visible to the type system as if they were 26 | regular methods. However, rather than in runtime, they are invoked during compilation. Each macro invocation is responsible 27 | for generating a piece of code that will replace the macro invocation and ultimately end up being compiled to bytecode. 28 | 29 | **TODO** 30 | * show an example of `def` macro 31 | * macro definition, implementation and invocation, separate compilation 32 | * introduce `Context`, `blackbox` at this point 33 | * quick explanation of `c.Tree` - note that it must be **typechecked** before being passed to macro 34 | * recursive macro expansion 35 | * macro bundles 36 | * blackbox and whitebox macros - IDE (not)friendliness 37 | 38 | -------------------------------------------------------------------------------- /chapters/Methods-and-operators.md: -------------------------------------------------------------------------------- 1 | # Methods and operators 2 | 3 | This section explores various syntactic features of Scala that can be used when defining and using methods and operators. Most of the things describe here can be quite "magic" and very confusing for Scala newcomers. That is why I decided to list them all fairly early in the guide. You can treat this section as a reference that you can come back later to if you meet any of strange syntactic creatures listed below. 4 | 5 | ## Collections - syntax basics 6 | 7 | Scala has a vast and fairly complicated collection library. It definitely deserves a separate section, so we won't go deeply into it, but we will use collections as an example to present some common patterns and syntactic peculiarities often used when working with them. 8 | 9 | ### The `apply` magic method 10 | 11 | #### Creating collections 12 | 13 | Usually, a collection in Scala can be created by simply "calling" a collection name and passing elements to it. For example, to create a `Vector` of elements 1, 2 and 3, we can write: 14 | 15 | ```scala 16 | val v = Vector(1,2,3) 17 | ``` 18 | ##### Note about `Vector` 19 | 20 | `Vector` in Scala is a very handy collection. It is an immutable sequence (a collection with defined order of elements) with near constant-time access to its elements by index. It also has very fast append and prepend operations while still staying fully immutable. This is because a vector created by appending something to other vector can share most of the data with its predecessor. Scala's `Vector` class implements this in some clever way. Immutable data structures with an ability to create new instances based on old ones by sharing most of the data are usually called *persistent data structures*. 21 | 22 | ##### Coming back to the syntax 23 | 24 | But what is actually happening in our code? We call `Vector` as if it was a function. It turns out that what we actually do is calling an `apply` method on an `object` called `Vector`: 25 | 26 | ```scala 27 | val v = Vector.apply(1,2,3) 28 | ``` 29 | 30 | This is simply because all methods named `apply` are treated "magically" by the Scala compiler. They can be used as if they were some kind of overloaded function call operator. That is why we can omit the name `apply` when calling `Vector.apply(1,2,3)` and simply write `Vector(1,2,3)`. 31 | 32 | You may also note that type inference is also working here. If we inspect the type of `v` (e.g. by hitting `Alt`+`=` in IntelliJ IDEA while having cursor on `v`), we will see that its type is `Vector[Int]`. Scala compiler has inferred the type of vector element from the values that we passed to the `apply` method. A completely desugared version of our invocation would look like this: 33 | 34 | ```scala 35 | val v = Vector.apply[Int](1,2,3) 36 | ``` 37 | ##### Note about companion objects 38 | 39 | It may also be somewhat strange to you that there is an object named `Vector` even though there is already a class named `Vector` (which represents the collection). Scala allows to define a class and an object (or a trait and an object) in the same file and give them the same name. Such object is then called a *companion object* of its class and the class is a *companion class* of its object. 40 | 41 | #### Accessing collection elements 42 | 43 | To access vector elements by index, Scala also uses the `apply` magic method. We can look into the definition of `Vector` class and we will see a method with such signature: 44 | 45 | ```scala 46 | // 'A' is the type of Vector elements 47 | def apply(index: Int): A 48 | ``` 49 | 50 | Note that this `apply` method is different from `apply` method we used before to create a vector. That one was defined in the `Vector` *object* but the one we're going to use now is defined in `Vector` *class*. This means that our `v` value has it. We can use it like here: 51 | 52 | ```scala 53 | val firstElement = v(0) 54 | ``` 55 | 56 | which is of course the same as: 57 | 58 | ```scala 59 | val firstElement = v.apply(0) 60 | ``` 61 | 62 | Due to existence of magic `apply` method, Scala doesn't need any special operator for array access (like square brackets in C or Java). Scala treats arrays just like any other collection: 63 | 64 | ```scala 65 | val args = Array("some", "args", "here") 66 | val firstArg = args(0) 67 | ``` 68 | 69 | ### The magic `update` method 70 | 71 | However, unlike `Vector`, arrays are mutable. We need a way to modify each element of the array. This syntax will do it: 72 | 73 | ```scala 74 | args(0) = "new first arg" 75 | ``` 76 | 77 | This works thanks to another magic method. Scala compiler will understand the code above as: 78 | 79 | ```scala 80 | args.update(0, "new first arg") 81 | ``` 82 | 83 | So everything works simply because `Array` has a method named `update` that takes two arguments. 84 | 85 | ## Methods == operators 86 | 87 | *Does Scala have operator overloading? Can we define custom operators in Scala?* 88 | 89 | The answer to questions above can be both "yes" and "no". In practice, it is "yes" - Scala allows us to define almost arbitrarily named operators. This can be even abused to write very unreadable Scala code full of bizarre symbolic names like `<++=` and the likes. 90 | 91 | However, the questions above can technically be answered "no". That's because Scala has no real distinction between methods and operators. They're the same thing. Operators are just methods. Even the most basic ones, like arithmetic or logical operators are simply methods defined in classes like `Int`, `Boolean`, `Double` etc. For example, addition operator for ints is declared as: 92 | 93 | ```scala 94 | def +(x: Int): Int 95 | ``` 96 | 97 | But you may think: *Methods and operators are still different. Methods are called using syntax with dot and parentheses (i.e. `obj.method(arg)`) while operators are used with infix syntax (`a + b`), aren't they?* Apparently, they are not. Both standard "method call" syntax and infix notation can be used for any method and operator. So, you can write `obj method arg` instead of `obj.method(arg)` and `a.+(b)` instead of `a + b`. It is just a good convention in Scala to use "method call" syntax for method with alphanumeric names and infix syntax for methods with symbolic names ("operators"). 98 | 99 | So there *really* is no distinction between methods and operators in Scala. 100 | 101 | ### Operator precedence 102 | 103 | Ok, so we know that `a + b` is translated to `a.+(b)`. But what if we write `a + b ++ c op d *+* e`? Which operator has the precedence? If Scala only allowed to overload well-known mathematical operators like `+` and `*`, this would not be a problem - it is well known that `*` has precedence over `+` etc. But Scala allows to define completely arbitrary operators, like in the example above. So how does it resolve such situations? 104 | 105 | Scala has somewhat peculiar rule for determining operator precedence. It does it by looking at the **first character** of the operator and then applies predefined priority for each one: 106 | 107 | * letters have the lowest priority 108 | * `|` 109 | * `^` 110 | * `&` 111 | * `<` `>` 112 | * `=` `!` 113 | * `:` 114 | * `+` `-` 115 | * `*` `/` `%` 116 | * all other symbolic characters have highest priority 117 | 118 | As you can see, the order is set up in such a way that well-known operator precedence for mathematical operators is retained. 119 | 120 | You don't have to remember that precedence order, but it is very important to remember that such rule exists. When we previously said that Scala allows you to define almost arbitrarily named operators, it may have seemed that it gives us a great freedom in making up fancy names for operators. But the fact that precedence has to be taken into account makes that freedom much more constrained. 121 | 122 | In general, be very careful when creating APIs with fancy operators. Or in fact - avoid them. They can easily lead to very cryptic code and is recommended only when you're carefully crafting some kind of well-documented DSL (domain specific language) and meaning of each operator is well-known in that domain. If you're in doubt, it's better to be conservative and stay with plain, alphanumeric names. This is an area of Scala that can be easily [abused](http://www.flotsam.nl/dispatch-periodic-table.html). 123 | 124 | ### Associativity 125 | 126 | OK, so we have the operator precedence set up. But how should `a + b + c` be understood? Is it `a + (b + c)` or `(a + b) + c`? 127 | 128 | By default, Scala operators are left-associative, so it is `(a + b) + c` 129 | 130 | ### Right associative operators 131 | 132 | Everything we have said so far has a **very important exception** - it is also a quite arbitrary and non-intuitive one, which makes it even more important to know as soon as possible. 133 | 134 | Scala operators whose names **end with a colon** (`:`) are treated specially. 135 | 136 | First, the infix notation itself is understood differently - the arguments are **flipped**, So `a +: b` is translated to `b.+:(a)`. To be very precise, it actually translated to: 137 | 138 | ```scala 139 | { 140 | // _x is just a placeholder - the compiler always chooses some unused, opaque name 141 | val _x = a 142 | b.+:(_x) 143 | } 144 | ``` 145 | 146 | The reason for such strange encoding done by the compiler is to ensure that `a` is evaluated before `b`. 147 | 148 | Associativity of colon-ending operators is also flipped. They are **right-associative**. So, `a +: b +: c` is actually `a +: (b +: c)`. 149 | 150 | ### Assignment operators 151 | 152 | *Note*: Some symbols which are usually called "operators" in other languages are called *delimiters* in Scala nomenclature. An example is the assignment symbol `=`. So, in context of this discussion, plain assignment is *not* an operator, but a delimiter. 153 | 154 | In one of the very simple code examples in the [first chapter](Dissection-of-Hello-World), we have noted that there are no pre-increment or post-increment operators in Scala, so we had to use `+=` to increment a variable: 155 | 156 | ```scala 157 | i += 1 158 | ``` 159 | 160 | We have also just said that all operators are methods, but if we look into the `Int` class, we won't find a declaration of `+=` there. So why does it work? 161 | 162 | When we use an infix operator that ends with a `=` character, e.g.: 163 | 164 | ```scala 165 | // <+> is an example, this can be arbitrary operator 166 | l <+>= r 167 | ``` 168 | 169 | and the compiler is unable to find actual method named `<+>=` on `l` then it translates the statement to: 170 | 171 | ```scala 172 | l = l <+> r 173 | ``` 174 | 175 | ### Unary operators 176 | 177 | Scala's unary prefix operators `!`, `~`, `+` and `-` are also translated to method calls. For example, negation: 178 | 179 | ```scala 180 | !something 181 | ``` 182 | 183 | is understood by compiler as 184 | 185 | ```scala 186 | something.unary_! 187 | ``` 188 | 189 | This way unary operators can also be overloaded. However, this applies only to the four listed operators - `!`, `~`, `+` and `-`. You cannot define any other unary operators. 190 | 191 | ### Multi-argument infix syntax 192 | 193 | Scala allows infix syntax even for methods with more than one argument. So you can write: 194 | 195 | ```scala 196 | javaMap put ("key", "value") 197 | ``` 198 | 199 | instead of 200 | 201 | ```scala 202 | javaMap.put("key", "value") 203 | ``` 204 | 205 | Unfortunately, this comes at a price. Parentheses in Scala are also used to denote *tuples*. So, by `("key", "value")` we may also mean a pair of two strings, `"key"` and `"value"`. For example, to add a key-value pair to a Scala mutable map, we could use its `+=` operator which takes a single argument - a pair. 206 | We'd like to do it like this: 207 | 208 | ```scala 209 | scalaMap += ("key", "value") // error 210 | ``` 211 | 212 | This won't work, because the compiler will understand it as `scalaMap.+=("key","value")` where we actually meant `scalaMap.+=(("key", "value"))`. In order to make the compiler happy, we need to wrap our pair in more parentheses: 213 | 214 | ```scala 215 | scalaMap += (("key", "value")) 216 | ``` 217 | 218 | ### Autotupling 219 | 220 | In other situations, Scala is doing exactly the opposite - it creates a tuple where we meant to pass multiple arguments. Such transformation is called *autotupling*. For example: 221 | 222 | ```scala 223 | println(1, 2, 3) 224 | ``` 225 | 226 | will be understood by the compiler as `println((1,2,3))`, because there's no `println` method with three parameters. This can be very confusing, so it's good to know as soon as possible. 227 | 228 | ## No-argument methods 229 | 230 | While talking about various syntaxes to call methods in Scala, it's also worth to mention that Scala differentiates between methods that have an _empty parameter list_ and methods that have _no parameter lists_: 231 | 232 | ```scala 233 | def emptyParamList() = ... 234 | def noParamLists = ... 235 | ``` 236 | 237 | Why is there such distinction and when to use both variants? 238 | 239 | Empty parameter list should be used when a method has some side effects (e.g. it invokes some I/O or modifies a variable). Method with no parameter lists should be used when it's *pure* (has no side effects). This is not enforced by Scala type system in any way, but it's a well-established, strong convention. 240 | 241 | The same rule applies to invocations of these methods. We would write: 242 | 243 | ```scala 244 | val x = emptyParamList() 245 | val y = noParamLists 246 | ``` 247 | 248 | However, Scala allows to omit the empty parameter list: 249 | 250 | ```scala 251 | val x = emptyParamList 252 | ``` 253 | 254 | This is for Java compatibility. Scala sees all no-argument Java methods as methods with empty parameter list. Some of them may be pure, but some may not. When calling Java methods, you should use empty parameter list only when the method has side effects. For example, you should not use it when calling getters: 255 | 256 | ```scala 257 | val cls = "stuff".getClass 258 | ``` 259 | 260 | If a method with empty param list is defined in Scala, you should never omit the empty param list when calling it. 261 | 262 | ## Summary 263 | 264 | Scala has many syntactic sugars that are meant to make the language more flexible and concise. Unfortunately, these sugars and arbitrary syntax rules can often make it very confusing and sometimes cause annoying ambiguities. -------------------------------------------------------------------------------- /chapters/Modularization-and-object-orientation.md: -------------------------------------------------------------------------------- 1 | ## Modularization and object orientation 2 | 3 | 4 | 5 | 6 | 7 | 8 | * traits 9 | * comparison to Java 8 interfaces 10 | * init code, but no constr params 11 | * multiple inheritance 12 | * overriding and linearization 13 | * initialization order problems, early initialization 14 | * the `abstract override` 15 | -------------------------------------------------------------------------------- /chapters/Notes.md: -------------------------------------------------------------------------------- 1 | ## String interpolations 2 | 3 | * String interpolations allow splicing arguments into string literals 4 | 5 | ```scala 6 | val name = "Fred" 7 | s"My name is $name, uppercased ${name.toUpperCase}" 8 | ``` 9 | * `s` is only one of the available interpolators, there are also `f`, and `raw` in standard library **MORE...** 10 | * It is possible to define custom interpolators - each interpolator may do whatever it wants with its arguments and 11 | String literal parts, it may also return arbitrary type as an interpolation result, not just a string 12 | * String interpolations suffer from some annoying syntactic quirks **MORE...** 13 | 14 | ## Multiline strings 15 | 16 | * It is possible to define multiline strings in Scala using triple-quotes 17 | 18 | val ms = 19 | """some long 20 | |multiline text here 21 | | "nothing" needs escaping 22 | | \ more stuff \ 23 | | 24 | |""".stripMargin 25 | 26 | * Multiline strings treat all characters literally, not just newlines - you don't have to escape anything 27 | * Multiline string interpolations are also possible 28 | 29 | ## Type system basics 30 | 31 | * Scala toplevel type is `Any` which has two direct subtypes - `AnyVal` and `AnyRef` 32 | * `AnyVal` is a common supertypes for non-nullable types which includes mostly equivalents of Java primitives. 33 | However, these types are more powerful than Java primitives, because they can be used as type parameters (generics). 34 | It is also not always guaranteed that they are represented as actual primitives in runtime (e.g. when they are used 35 | as collection elements). 36 | * Scala standard library defines implicit conversions between primitives and `java.lang.*` boxed equivalents of primitives. 37 | * It is possible to define custom types that extend `AnyVal` - value classes **ADVANCED, MORE LATER** 38 | * `AnyRef` is equivalent to `java.lang.Object`. This is the base type for all nullable types - classes, traits, Java 39 | interfaces. 40 | * All nullable types have also a common bottom *subtype*, the `Null` type 41 | * All types have common bottom type, the `Nothing` type - the only valid expression that has type `Nothing` is something 42 | that throws an exception (or is elided by compiler). 43 | * We may request that a compiler treats some expression as a particular type using type ascription, e.g. `0: Long` 44 | * Type ascription is can be used for upcasting and forcing usage of implicit conversions. 45 | However, it must be a "safe cast" - the compiler will report an error when it is unable to match types or 46 | find an implicit conversion. 47 | 48 | ## When you're force to go untyped 49 | 50 | * The syntax for ugly, unsafe cast is `x.asInstanceOf[SomeType]` - should obviously be avoided 51 | * `null.asInstanceOf[T]` *always* works - if `T` is primitive, the value will be appropriately typed "zero" 52 | * `x.asInstanceOf[AnyRef]` *always* works - it simply forces boxed representation of `x` 53 | * The equivalent of Java's `instanceof` operator in scala is `x.isInstanceOf[SomeClass]` - should be avoided even more 54 | * Pattern matching is also doing runtime type checks and has nicer syntax **LATER** 55 | * Class objects in Scala are accessed using `classOf[SomeClass]` syntax (equivalent of Java `SomeClass.class`) 56 | * **SUBJECTIVE** `AnyVal` and `AnyRef` are pretty much useless (to be used in APIs). 57 | If you really need to use toplevel type (i.e. in situations similar to when you use `java.lang.Object` in Java), simply use `Any`. 58 | If some Java API requires you to pass `java.lang.Object`, just cast your `Any` to `AnyRef` (which as we mentioned earlier, 59 | always works). 60 | 61 | ## Collection basics - syntax 62 | 63 | * There is no separate operator for accessing array/collection/map elements by index/key 64 | * Instead, when something has a method named `apply` (a magic method), it may be called with just parens (i.e. 65 | sort-of overloaded call operator) 66 | * Most collections use `apply` for element-by-index access, i.e. `someArray(0)`, `someMap("key")` 67 | * The `apply` method is also defined on companion objects of collection and can be used for colleciton creation, e.g. 68 | `Array(1,2,3)` is actually `Array.apply(1,2,3)` where `Array` in this context is a companion object of `Array` class. 69 | Type inference also works here to recognize that it's actually `Array.apply[Int](1,2,3)` 70 | * In order to modify some element of a mutable collection, you can say `someArray(0) = 5` which actually desugars to 71 | `someArray.update(0, 5)` - the `update` method is another magic method 72 | 73 | ## Operators and infix syntax 74 | 75 | * In scala, there is no distinction between methods and operators 76 | * Every method that takes single argument may be called with an infix syntax 77 | * An operator is just a method with symbolic name 78 | * Methods taking multiple parameters may also be called using infix syntax, but it's annoying when you want to pass 79 | a tuple as an argument 80 | * There is also a "complementary" syntactic sugar called autotupling which may automatically build a tuple out of 81 | multiple arguments when they are passed to a method accepting single argument. 82 | * Operator precedence is governed by the first character in operator/method name **LINK** 83 | * Normally, operators called with infix syntax are left-associative 84 | * **IMPORTANT** Operators which have a colon `:` at the end are treated specially - the arguments are flipped when 85 | translating to a standard method call and they are right-associative 86 | * Syntactic convention: use infix syntax for operators (methods with symbolic names) and standard syntax for methods 87 | with alphanumeric names 88 | * Methods with no parameter lists vs methods with single but empty parameter list 89 | 90 | ## Basic functional stuff 91 | 92 | * Function objects 93 | * Lambdas - full and shorter syntax 94 | * Eta expansion 95 | * Higher-order functions 96 | * Closures - closing over variables, this and return 97 | -------------------------------------------------------------------------------- /chapters/Packages-and-imports.md: -------------------------------------------------------------------------------- 1 | ## Packages and imports 2 | 3 | ### Files 4 | 5 | In *Java*, every source file must contain at most one public class and the file must have the same name as that class. 6 | 7 | Scala has no such restriction. Scala source file may contain many classes, objects and traits and there are no restrictions in terms of their visibility. However, it is a good practice to retain the Java convention and put large classes in their dedicated source files. But since it's not uncommon for Scala classes to be defined in single line, it's good that the language allows keeping them together. 8 | 9 | Also, if you recall *algebraic types*, you know that they rely on the `sealed` modifier which - when applied to a trait or class - requires that all direct subclasses must be defined in the same file. This way Scala uses files as containers for sealed class hierarchies. 10 | 11 | ### Package nesting 12 | 13 | We'll look back at *Java* once again. In Scala's predecessor, packages seem to have a hierarchical structure (e.g. `com.google`, `com.google.guava`). But it's mostly just their names that form some kind of hierarchy. Packages themselves don't nest, i.e. `com.google.guava` is not contained in `com.google` - it's a completely separate package. This means that full import is required when some classes from the two packages want to refer to each other. Also, classes in `com.google` have no access to package-protected definitions in `com.google.guava` and vice versa. 14 | 15 | Scala by default behaves the same way, but it is possible to change it. Everything depends on how the package is declared at the beginning of `.scala` file. 16 | 17 | Let's look at an example. Suppose our project's source code is split into two primary packages, `com.software.theproject.core` and `com.software.theproject.gui`. First package contains the `com.software.theproject.core.Utils` class which is used class `com.software.theproject.gui.PrettyWindow` class from the other package. Here's how `PrettyWindow.scala` is defined: 18 | 19 | ```scala 20 | package com.software.theproject.gui 21 | 22 | import com.software.theproject.core.Utils 23 | 24 | class PrettyWindow { 25 | Utils.failWith("Not implemented yet!") 26 | } 27 | ``` 28 | 29 | In order to use `Utils`, `PrettyWindow` needs a full, explicit import. But in the snippet below, this is no longer the case: 30 | 31 | ```scala 32 | package com.software.theproject 33 | package gui 34 | 35 | import core.Utils 36 | 37 | class PrettyWindow { 38 | Utils.failWith("Not implemented yet!") 39 | } 40 | ``` 41 | 42 | As you can see, we have split the package clause into two separate lines. Such construct is called *chained package clause*. `PrettyWindow` is still in the same package as before (`com.software.theproject.gui`) but now it can directly access anything from `com.software` without import. That's why we were able to shorten `import com.software.theproject.core.Utils` to just `import core.Utils` - the `core` package is visible directly as a member of `com.software`. 43 | 44 | In other words, splitting the package clause into multiple lines allows us to see identifiers from all intermediate packages (each line of package clause represents "intermediate" package), not just from exactly the full package that our file is defined in. 45 | 46 | Almost all real-life projects in Java or Scala have their dedicated toplevel package, i.e. every source file in that project is (directly or not) contained in that package. For example, in our snippet it would be `com.software.theproject`. Common usage of chained package clauses is to split the package clause at that toplevel package, just like we did it in above snippet. 47 | 48 | ### Multiple packages in single file 49 | 50 | As we already mentioned, Scala allows you to put many classes in the same file. But it goes even further with taking down file restrictions - you can define contents of multiple *packages* in a single file. For example: 51 | 52 | ```scala 53 | package com.software.theproject 54 | 55 | package utils { 56 | // contents of package `com.software.theproject.utils` 57 | 58 | package subutils { 59 | // contents of package `com.software.theproject.utils.subutils` 60 | } 61 | } 62 | 63 | package core { 64 | // contents of package `com.software.theproject.core` 65 | } 66 | ``` 67 | 68 | ### Packages vs directories 69 | 70 | Standard Java convention says that source files should be placed in a directory that corresponds to package name. This is the same in Scala, but gets a bit more complicated for source files with multiple packages. In such case it seems natural to put the file in the package that contains every other subpackage defined in that file. For example, the file from previous section would be put into `com/software/theproject` directory. 71 | 72 | ### Package objects 73 | 74 | In general, packages may contain only classes, objects, traits and other packages. It's impossible to put a method or field directly into a package. 75 | 76 | However, Scala has a construct that somewhat simulates such situation - *package objects*. 77 | 78 | Here's a sample definition of package object associated with the `com.software.theproject` package. The file is named `package.scala` and is in the `com/software/theproject` directory. 79 | 80 | ```scala 81 | package com.software 82 | 83 | package object theproject { 84 | def utilFunction(utilParam: String): Int = ??? 85 | 86 | val Constant = "constant" 87 | } 88 | ``` 89 | 90 | A package object is technically just an object named `` `package` `` (you can actually refer to it in code using ``com.software.theproject.`package` ``), but is treated a bit specially by the compiler. 91 | 92 | All members of the package object are seen as if they are members of the package itself. For example, the `Constant` defined in above snippet can be accessed with `com.software.theproject.Constant`. Of course, you can also import it in the same way as a class would be imported. 93 | 94 | Additionally, remember that package nesting and chained package clauses can make members of package automatically visible. This applies in the same way to package object members. For example: 95 | 96 | ```scala 97 | package com.software.theproject 98 | package core 99 | 100 | object CoreUtil { 101 | // reference to `Constant` needs no import or qualification 102 | val ConstantUpper = Constant.toUpperCase 103 | } 104 | ``` 105 | 106 | ## Imports 107 | 108 | Scala introduces a lot of new features to `import` constructs as compared to Java. We'll try to walk through them in this section. 109 | 110 | #### Regular package member imports 111 | 112 | The most common type of import you'll encounter in Java (and probably in Scala, too) is an `import` clause at the beginning of the file (just after `package` clause or clauses) which imports some class or interface (trait in Scala) from a package. 113 | 114 | ```scala 115 | package com.software.theproject 116 | package core 117 | 118 | import java.util.List 119 | import java.util.ArrayList 120 | ``` 121 | 122 | This looks the same in Java and Scala, except that you don't need semicolons. 123 | 124 | #### Importing a package 125 | 126 | Here's a first improvement that Scala introduces over Java: you can import a package *itself* and then refer to it just with its name: 127 | 128 | ```scala 129 | package com.software.theproject 130 | package core 131 | 132 | import java.io 133 | 134 | object CoreUtil { 135 | def newStringWriter = new io.StringWriter 136 | } 137 | ``` 138 | 139 | #### Relative imports 140 | 141 | You can also import relatively, i.e. from things that are already visible, e.g. thanks to some previous imports. 142 | 143 | ```scala 144 | package com.software.theproject 145 | package core 146 | 147 | import java.io 148 | import io.StringWriter 149 | 150 | object CoreUtil { 151 | def newStringWriter = new StringWriter 152 | } 153 | ``` 154 | 155 | #### Local imports 156 | 157 | Imports can be used in arbitrary places in your source files, not just at the beginning of the file: 158 | 159 | ```scala 160 | package com.software.theproject 161 | package core 162 | 163 | import java.io 164 | 165 | object CoreUtil { 166 | import io.StringWriter // this import is visible only inside the `CoreUtil` object 167 | def newStringWriter = new StringWriter 168 | 169 | def newStringReader(str: Striong) = { 170 | import io.StringReader // this import is visible only inside the `newStringReader` method 171 | new StringReader(str) 172 | } 173 | } 174 | ``` 175 | 176 | Local imports are very nice tool to minimize the impact of your imports and protect yourself from accidentally polluting too wide namespaces with imports that you need only in small, local context. That's especially important when using *wildcard* imports (see later). 177 | 178 | #### Imports from values 179 | 180 | Obviously, you can import things from packages and objects. But you can also import from `val`s and `lazy val`s: 181 | 182 | ```scala 183 | package com.software.theproject 184 | package core 185 | 186 | import java.util.ArrayList 187 | 188 | object CoreUtil { 189 | def createList(a: Int, b: Int, c: Int): ArrayList[Int] = { 190 | val result = new ArrayList[Int] 191 | import result.add 192 | add(a) 193 | add(b) 194 | add(c) 195 | result 196 | } 197 | } 198 | ``` 199 | 200 | Imports from packages, objects and `val`s can be arbitrarily chained. For example if package `p` has `object x` which has a member `val y` which has a method `meth`, you can directly `import p.x.y.meth`. 201 | 202 | #### Multi imports 203 | 204 | You can import multiple identifiers in a single line: 205 | 206 | ```scala 207 | import java.io.{StringWriter, StringReader} 208 | ``` 209 | 210 | #### Import aliases 211 | 212 | You can import an identifier under a different name. 213 | 214 | This is useful for example when you want to concisely refer to contents of some package or object, but you don't want to import everything from it: 215 | 216 | ```scala 217 | import java.{lang => jl} 218 | 219 | object Main { 220 | def takeJavaLong(n: jl.Long): Unit = ??? 221 | } 222 | ``` 223 | 224 | This is especially useful when direct import would introduce a name conflict (e.g. `Long` can be `scala.Long` or `java.lang.Long`). 225 | 226 | #### Wildcard imports 227 | 228 | Finally, you can import *all* members of some package, object, `val, etc. by using wildcard import: 229 | 230 | ```scala 231 | import java.io._ 232 | ``` 233 | 234 | Be careful with wildcard imports - you can easily write unmaintainable code with them. A good practice is to limit the usage of wildcard imports to local imports (e.g import all constants of some Java enum in a local code block). 235 | 236 | You can join wildcard imports with import aliases to import everything, but some of the things under a different name: 237 | 238 | ```scala 239 | // import everything from java.io, but StringReader under an alias SR 240 | import java.io.{StringReader => SR, _} 241 | ``` 242 | 243 | There's also a special syntax that allows you to import everything *except* some things, e.g. 244 | 245 | ```scala 246 | // import everything from java.io except StringWriter and StringReader 247 | import java.io.{StringReader => _, StringWriter => _, _} 248 | ``` -------------------------------------------------------------------------------- /chapters/Traits.md: -------------------------------------------------------------------------------- 1 | Traits are a rich and complex feature of Scala and thus deserve a separate section. 2 | 3 | Traits, defined with the `trait` keyword are something between Java interfaces and abstract classes. In fact, we'll see that they can do *almost* everything that abstract classes can, but at the same time they fulfill all roles of Java interfaces. 4 | 5 | ### Traits as interfaces 6 | 7 | In their simplest form, `trait`s are equivalent to Java interfaces: 8 | 9 | ```scala 10 | trait StuffHandler { 11 | def handleStringStuff(stuff: String): Unit 12 | 13 | def handleIntStuff(stuff: Int): Unit 14 | } 15 | ``` 16 | 17 | In this form a `trait` contains only public, abstract methods. Just like with Java interfaces, such a trait may extend multiple other traits and Java interfaces (which are actually seen by Scala type system as traits) and can be implemented by classes. A class may implement multiple interfaces. 18 | 19 | Also, note that such Scala trait will compile to regular Java interface in bytecode. 20 | 21 | ### Method implementations 22 | 23 | In Java 8, interface methods may have default implementations ("default methods"). Scala can do this too (although they're not called "default methods"): 24 | 25 | ```scala 26 | trait StuffHandler { 27 | def handleStringStuff(stuff: String): Unit 28 | 29 | // just handle Int stuff by converting it to String 30 | def handleIntStuff(stuff: Int): Unit = 31 | handleStringStuff(stuff.toString) 32 | } 33 | ``` 34 | 35 | **NOTE**: Traits were designed and implemented long before Java 8 and therefore compile to different bytecode than Java 8 deafult methods (at least for Scala 2.11). 36 | 37 | At this point, however, the similarities between Java interfaces and Scala traits end. Now we're going to show capabilities of traits which will lead us much closer to abstract classes. 38 | 39 | ### Inheritance syntax 40 | 41 | Scala has two keywords for expressing inheritance (which includes mixing in traits): `extends` and `with`. However, as we will explain shortly, be aware that `with` is not an equivalent of Java's `implements`. 42 | 43 | When a class extends another class, it uses `extends`: 44 | 45 | ```scala 46 | class Base 47 | class Impl extends Base 48 | ``` 49 | 50 | When it wants to mix in some traits above the base class, it does it using `with` keyword: 51 | 52 | ```scala 53 | class Base 54 | trait Logging 55 | trait Utils 56 | class Impl extends Base with Logging with Utils 57 | ``` 58 | 59 | However, a class doesn't need to declare its abstract class, it can directly mix in traits: 60 | 61 | ```scala 62 | trait Logging 63 | trait Utils 64 | class Impl extends Logging with Utils 65 | ``` 66 | 67 | As you can see, we're still using the `extends` keyword even though we don't explicitly inherit from any base class. Also, when a trait inherits from other traits, it uses `extends`, too: 68 | 69 | ```scala 70 | trait Logging 71 | trait Utils 72 | trait Commons extends Logging with Utils 73 | ``` 74 | 75 | It just seems like `extends` is simply always used as for the first thing that we inherit (regardless of whether it's a class or trait) and if we have more traits to mix in then we use the `with` keyword. 76 | 77 | This rule may seem somewhat arbitrary and unintuitive. However, there's a fairly simple way to make sense of it. Line like this one: 78 | 79 | ```scala 80 | class Impl extends Base with Logging with Utils 81 | ``` 82 | 83 | should actually be read as: 84 | 85 | ```scala 86 | // this is actually not a correct Scala code, but it's good to think about it that way 87 | class Impl extends (Base with Logging with Utils) 88 | ``` 89 | 90 | So it's good to think about it as if we're mixing `Base`, `Logging` and `Utils` together and then `Impl` class extends that unnamed mix. 91 | 92 | **NOTE**: The order of traits mixed in by class or trait definition **does matter** - you will soon see why. Also, base class must always come immediately after `extends` keyword. 93 | 94 | ##### Anonymous mix-ins 95 | 96 | It's also possible to instantiate anonymous classes which mix in multiple traits. For example: 97 | 98 | ```scala 99 | val b: Base = new Base with Logging with Utils 100 | ``` 101 | 102 | ##### Intersection types 103 | 104 | You may encounter the `with` keyword being used in another context - in type declarations. For example, you may see something like this: 105 | 106 | ```scala 107 | val ab: A with B = ??? 108 | ``` 109 | 110 | Such type should be read as "something that is an instance of both `A` and `B`". Unfortunately, usage of `with` keyword is a bit accidental here, because its meaning is substantially different than in inheritance syntax. In particular, when `with` is used in a type declaration `A with B` then: 111 | * `A` and `B` can be arbitrary types, not just classes or traits 112 | * the order of types does not matter 113 | 114 | For example, if `A` and `B` are traits, this code will compile: 115 | 116 | ```scala 117 | val awb: A with B = new B with A 118 | ``` 119 | 120 | ### Much more than public methods 121 | 122 | Unlike in Java interfaces, methods don't have to be public - they may have any access modifier that a method may have. They can also be `final` (Java 8 default methods cannot) 123 | 124 | ```scala 125 | trait SafeStuffHandler { 126 | final def handleSafely(stuff: String): Unit = 127 | try handleUnsafely(stuff) catch { 128 | case NonFatal(t) => t.printStackTrace() 129 | } 130 | 131 | protected def handleUnsafely(stuff: String): Unit 132 | } 133 | ``` 134 | 135 | ### Data and state 136 | 137 | Just like classes, `trait`s in Scala can have data and state in the form of `val`s, `lazy val`s, `var`s and `object`s. They may be abstract or concrete, they may implement, override, be final, etc. 138 | 139 | This is a real game changer, because now we can no longer think about traits as slightly better interfaces. A trait that has state and data can now hold significant amount of inheritable implementation and no longer represent just some abstraction. 140 | 141 | That's why we usually don't say that traits are "implemented" but rather "mixed in". A common usage of traits is to split large class implementation into a set of separate traits and "mix" them into a class. This shows a fundamental conceptual difference between Java interfaces and Scala traits: interfaces are for expressing abstraction (which may have some default behavior to allow easier interface evolution) while traits are much more capable of being reusable pieces of implementation. 142 | 143 | **TODO** some nice example? 144 | 145 | ### Traits can extend classes 146 | 147 | Traits can extend other traits and thus call, implement, refine and override inherited members. But a trait can also inherit from class and do the same things with its members (almost): 148 | 149 | ```scala 150 | abstract class BaseHandler { 151 | def handle(stuff: String): Int = println("Handling!") 152 | } 153 | trait LoggingHandler extends BaseHandler { 154 | override def handle(stuff: String): Int = { 155 | println(s"About to handle $stuff") 156 | super.handle(stuff) 157 | } 158 | } 159 | 160 | abstract class BetterHandler extends BaseHandler { 161 | def doBetterWork(): Unit = println("Doing it!") 162 | } 163 | 164 | class ConcreteHandler extends LoggingHandler 165 | class BetterConcreteHandler extends BetterHandler with LoggingHandler 166 | ``` 167 | 168 | The `LoggingHandler` trait is a simple example of trait created for some code reuse. Every class that extends `BaseHandler` and wants to add some logging to its `handle` method can just mix in the `LogginHandler` trait. 169 | 170 | ### Multiple class inheritance? 171 | 172 | You may think: if a trait can extend a class and a class can mix in multiple traits, does that mean that a class can have multiple (unrelated) base classes? 173 | 174 | The answer is **no**. Every class in Scala can still have only one (direct) superclass. In our previous example, the superclass of `ConcreteHandler` is `BaseHandler` (implicitly inherited as a result of mixing in `LoggingHandler`) and superclass of `BetterConcreteHandler` is `BetterHandler` (explicitly inherited). There is no conflict, even though `LoggingHandler` extends `BaseHandler` because `BetterHandler` also extends `BaseHandler`. However, if we were to write: 175 | 176 | ```scala 177 | abstract class BaseHandler 178 | abstract class Unrelated 179 | trait LoggingHandler extends BaseHandler 180 | // error! 181 | class ConcreteHandler extends Unrelated with LoggingHandler 182 | ``` 183 | 184 | We would get a compilation error, because `BaseHandler` and `Unrelated` can't be both superclasses of `ConcreteHandler`. 185 | 186 | ### Initialization code 187 | 188 | We have already announced that traits can have `val`s, `lazy val`s, `var`s and `object`s. This means that traits may have some initialization code associated with them. 189 | 190 | In fact, traits have a "constructor". Just like in a class or object, the body of a trait can have some code which will be executed when an instance is created. 191 | 192 | However, the fundamental difference from classes is that **trait constructor cannot take parameters**. 193 | 194 | ### Linearization 195 | 196 | Multiple implementation inheritance and the fact that traits have an initialization code ("constructor") creates some serious challenges: 197 | 198 | * the diamond problem: what happens when a class mixes in the same trait multiple times? 199 | * initialization order: in what order should base class constructors and trait "constructors" be executed when an instance is created? 200 | * conflict resolution: how to resolve conflicts between two inherited implementations of the same method? 201 | 202 | Scala resolves these issues with a mechanism called *linearization*. It is an algorithm that takes all base classes and mixed in traits of some class and orders them into a linear sequence. The way this happens is well defined and deterministic. 203 | 204 | Linearization algorithm can be summarized a bit more formally as follows: having a class `C` which `extends B1 with B2 with ... with Bn`, the linearization of class `C` is defined as follows: 205 | 206 | ``` 207 | lin(C) = C >+ lin(Bn) >+ lin(Bn-1) >+ ... >+ lin(B1) 208 | ``` 209 | 210 | where the `>+` operator denotes concatenation that additionally removes all elements from its left-side operand which are duplicated in the right-side operand. 211 | 212 | Let's see an example: 213 | 214 | ```scala 215 | trait A 216 | trait B extends A 217 | trait C extends A 218 | trait D 219 | class E extends B with C with D 220 | ``` 221 | 222 | So: 223 | ``` 224 | lin(E) = E >+ lin(D) >+ lin(C) >+ lin(B) 225 | lin(D) = D 226 | lin(A) = A 227 | lin(C) = [C, A] 228 | lin(B) = [B, A] 229 | // here we drop the duplicated A from the left side 230 | lin(C) >+ lin(B) = [C,A] >+ [B,A] = [C,B,A] 231 | lin(E) = [E, D, C, B, A] 232 | ``` 233 | 234 | **NOTE**: actually, `lin(E)` is `[E, D, C, B, A, AnyRef, Any]` but we omitted automatic `Any` and `AnyRef` for brevity. 235 | 236 | Note that this algorithm makes it clear why the order of declared mixed in traits is important! 237 | 238 | Linearization gives us fairly clear answers to all three questions we had earlier: 239 | 240 | ### The diamond problem 241 | 242 | Linearization removes duplicates, so that state and data from the same trait cannot be inherited multiple times independently. 243 | 244 | ### Initialization order 245 | 246 | Linearization unambiguously resolves initialization order - constructors and trait initializers are invoked one after another, in the reverse initialization order, i.e. starting with `Any` and ending with the constructor of the instantiated class itself. 247 | 248 | ```scala 249 | trait A { print("A") 250 | trait B extends A { print("B") } 251 | trait C extends A { print("C") } 252 | trait D { print("D") } 253 | class E extends B with C with D { print("E") } 254 | ``` 255 | 256 | In the above example, linearization of `E` is `[E,D,C,B,A]` and so invoking `new E` would print 257 | 258 | ``` 259 | ABCDE 260 | ``` 261 | 262 | ### Conflict resolution 263 | 264 | When inheriting multiple implementations of the same method from more than one base trait, then not only linearization order is important but also the usage of `override` and `final` keywords in conflicting implementations. 265 | 266 | Example: 267 | 268 | ```scala 269 | trait A { 270 | def someString: String 271 | } 272 | trait B extends A { 273 | def someString = "B" 274 | } 275 | trait C extends A { 276 | def someString = "C" 277 | } 278 | ``` 279 | 280 | Now, if we create a class that mixins both `B` and `C`, we'll get a compilation error: 281 | 282 | ```scala 283 | // error: class D inherits conflicting members 284 | class D extends B with C 285 | ``` 286 | 287 | There are multiple ways this conflict can be resolved, either by leaving the job to linearization or doing it explicitly: 288 | 289 | * Override in class `D`: 290 | 291 | ```scala 292 | class D extends B with C { 293 | override def someString = "D" 294 | } 295 | ``` 296 | * Override in class `D` and refer to super implementation: 297 | 298 | ```scala 299 | class D extends B with C { 300 | override def someString = super.someString 301 | } 302 | ``` 303 | The `super` keyword will choose first implementation of `someString` available in the linearization chain. In this case, the linearization is `[D,C,B,A]` and because of that, implementation from `C` will be used. 304 | Of course, the implementation of overriding method is not limited to calling super implementation - it may contain arbitrary code and call super implementation at any point. 305 | * Override in class `D` and explicitly refer to particular super implementation: 306 | 307 | ```scala 308 | class D extends B with C { 309 | override def someString = super[B].someString 310 | } 311 | ``` 312 | In this case, we have used a new syntax - qualified `super` keyword - to explicitly state that we want to use implementation from trait `B`. 313 | * Add an `override` modifier to implementation in `C`: 314 | ```scala 315 | trait C extends A { 316 | override def someString = "C" 317 | } 318 | ``` 319 | By adding an `override` modifier to `someString` method in trait `C`, we say that "implementation in `C` has priority over whatever comes after it in linearization order". In our case the linearization is `[D,C,B,A]` (`C` is before `B`) and that's why `C`'s implementation has priority over `B`'s. However, scala compiler required us to explicitly allow such overriding with an `override` keyword. 320 | 321 | If we added the `override` to implementation in `B` instead of `C` or changed `D` to extend `C with B` instead of `B with C`, this would start to fail again. We could also add `override` to both implementations - this way we could declare classes that extend both `B with C` and `C with B` and linearization would always resolve the conflict automatically. 322 | 323 | #### The undetermined meaning of `super` 324 | 325 | Previous examples for conflict resolution have shown a very peculiar property of Scala's `super` keyword: when some trait refers to a `super` implementation, it does not know where that implementation is until some class actually mixes in that trait and linearization takes its final word. This is a serious semantic difference from Java and can be very surprising. 326 | 327 | In particular, note that a trait `B` defined like this: 328 | 329 | ```scala 330 | trait A { def method: String = "A" } 331 | trait B extends A { override def method: String = super.method + "B" } 332 | ``` 333 | 334 | may not assume that the `super.method` will refer to `super[A].method` despite the fact that `A` is a direct supertype of `B`. When some class mixes in `B`, there may be another trait put "in between" `A` and `B`. 335 | 336 | #### `abstract override` 337 | 338 | We have just shown that when a trait overrides a method from its supertype and refers to super implementation, someone may provide that super implementation later - at the moment our trait is mixed in to a class. This in particular means, that when the trait is defined, the super implementation may not be there at all! 339 | 340 | This means that the trait can actually *override* an *abstract* method. Scala has a special combination of keywords for that: `abstract override`. Example: 341 | 342 | ```scala 343 | trait Abs { 344 | def someString: String 345 | } 346 | trait AppendMoreStuff extends Abs { 347 | abstract override def someString = super.someString + " and more stuff" 348 | } 349 | ``` 350 | 351 | We have overridden an abstract method and at the same time referred to `super` implementation. That implementation is not yet there - a (non-abstract) class which finally mixes in our `AppendMoreStuff` trait must "inject" some implementation of `someString` under `AppendMoreStuff`. For example: 352 | 353 | ```scala 354 | trait AbsImpl extends Abs { 355 | def someString = "Impl" 356 | } 357 | class Klass extends AbsImpl with AppendMoreStuff 358 | ``` 359 | 360 | The value returned by `someString` method on class `Klass` would return `Impl and more stuff`. 361 | 362 | The fact that a trait is not necessarily bound to any super implementation gives us an ability to reuse traits and compose them in many different configurations. Traits are "put over" another or "stacked" in potentially arbitrary order. That is why you may often hear about *stackable traits* when reading about Scala. 363 | 364 | #### `super` references to classes 365 | 366 | **TODO** 367 | 368 | ### Pitfalls and limitations of traits 369 | 370 | #### Initialization traps 371 | 372 | Here's an example of one of the most common pitfalls of inheritance in Scala (or maybe even entire Scala language). It deals with the initialization order and the fact that `val`s can be abstract and overridden. 373 | 374 | ```scala 375 | trait A { 376 | val someString: String 377 | val someStringUpper = someString.toUpperCase 378 | } 379 | class B extends A { 380 | val someString = "String from B" 381 | } 382 | ``` 383 | 384 | Now, what will happen when we instantiate `B`? It turns out, we'll get a `NullPointerException` at `someString.toUpperCase`! Why is that? 385 | 386 | This is a direct consequence of initialization order of `B`. From linearization we know that initialization code of `A` will execute before constructor of `B`. So here's exactly what happens when we try to invoke `new B`: 387 | 388 | 1. New object is created. No constructors or initializers have been invoked yet. The fields that hold values of `someString` and `someStringUpper` are therefore `null`. 389 | 2. Initializer of `A` is invoked. It tries to assign an initial value to `someStringUpper` and evaluates `someString.toUpperCase` which unfortunately fails with a `NullPointerException` because `someString` didn't get the chance to be initialized by constructor of `B`. 390 | 3. If it weren't for the NPE, constructor of `B` would be invoked here and it would assign the initial value to `someString`. 391 | 392 | So, how do we fix it? There are several ways, but all have their limitations: 393 | 394 | * Implement the abstract `val` with a constructor parameter: 395 | 396 | ```scala 397 | trait A { 398 | val someString: String 399 | val someStringUpper = someString.toUpperCase 400 | } 401 | class B(val someString: String) extends A 402 | ``` 403 | Fields that have their values from constructor parameters are initialized *before* super constructors and initializers, that's why this works. 404 | * Implement your abstract `val` with a `lazy val`: 405 | 406 | ```scala 407 | trait A { 408 | val someString: String 409 | val someStringUpper = someString.toUpperCase 410 | } 411 | class B extends A { 412 | lazy val someString = "String from B" 413 | } 414 | ``` 415 | This works, but note that this also causes `someString` to be initialized *before* the constructor of `B` is invoked. Therefore, you must be careful not to refer to something that is initialized by this constructor (e.g. another `val` defined in class `B`) in the initial value of your `lazy val`. 416 | For example, if you do this: 417 | 418 | ```scala 419 | class B extends A { 420 | lazy val someString = s"Number is $someNumber" 421 | val someNumber = 42 422 | } 423 | ``` 424 | then the NPE comes back, but at a different place. 425 | 426 | Also, notice an unintuitive fact: `lazy` keyword is normally supposed to *delay* the initialization of a field, but here it does the opposite - it actually makes it happen *earlier* than normally. 427 | * Make the non-abstract member (i.e. `someStringUpper`) a `lazy val` or `def`: 428 | 429 | ```scala 430 | trait A { 431 | val someString: String 432 | lazy val someStringUpper = someString.toUpperCase 433 | } 434 | class B extends A { 435 | val someString = "String from B" 436 | } 437 | ``` 438 | However, in order for this to work, you must ensure that nobody uses `someStringUpper` before constructor of `B` initializes `someString`. For example, if you do this: 439 | 440 | ```scala 441 | trait A { 442 | val someString: String 443 | lazy val someStringUpper = someString.toUpperCase 444 | val inBrackets = s"[$someStringUpper]" 445 | } 446 | ``` 447 | then everything will fail again. 448 | * As an absolute last resort, use an *early initializer*: 449 | 450 | ```scala 451 | trait A { 452 | val someString: String 453 | lazy val someStringUpper = someString.toUpperCase 454 | } 455 | class B extends { 456 | val someString = "String from B" 457 | } with A 458 | ``` 459 | This is a bizarre syntax that we have not yet shown in this guide. It forces the `someString` field to be initialized before initialization code of `A` is executed. This feature is a very dark corner of Scala and should be avoided as much as possible. If you find yourself using it, you should probably look again at your code and find a way to redesign it so that it doesn't depend so much on initialization order. 460 | 461 | #### Calling protected methods from classes 462 | 463 | **TODO** 464 | 465 | ### Self-type annotations 466 | 467 | Self-type annotation is a Scala construct that you can use to put a requirement on a trait or abstract class. This requirement lets you express that any non-abstract class that mixes in your self-annotated trait must 468 | also mix in some other trait or (more generally) extend some type. 469 | 470 | For example: 471 | 472 | ```scala 473 | trait A 474 | trait B { this: A => } 475 | ``` 476 | 477 | Now: 478 | * any non-abstract class extending `B` is required to also extend `A` 479 | * any trait or abstract class extending `B` which does not also extend `A` must repeat this requirement in its own self type annotation 480 | 481 | Examples: 482 | 483 | ```scala 484 | class C1 extends A with B // OK 485 | class C2 extends B with A // OK 486 | class C3 extends B // error! extending B without extending A 487 | trait C4 extends A with B // OK 488 | trait C5 extends B { this: A => } // OK, repeating the requirement 489 | trait C6 extends B // error! extending B without extending or requiring A 490 | ``` 491 | 492 | Self type annotation lets you use API of self-type inside the outer trait, e.g. 493 | 494 | ```scala 495 | trait Logging { 496 | def log(msg: String): Unit 497 | } 498 | trait Handler { this: Logging => 499 | def handle(stuff: String): Unit = 500 | log(s"Handling $stuff") // using method from Logging trait 501 | } 502 | ``` 503 | 504 | In other words, self type annotation is a way for a trait or abstract class to declare a "dependency" but without creating an explicit inheritance and subtyping relation. So you can think of it as a "weaker" form of inheritance - it does some of the things that inheritance does but not all of them. That makes it somewhat more flexible. 505 | 506 | Having: 507 | ```scala 508 | trait Person 509 | trait Childish extends Person 510 | trait Selfish { this: Person => } 511 | ``` 512 | 513 | The differences between self-type annotation (`Selfish`) and inheritance (`Childish`) are: 514 | * `Childish` is a subtype of `Person` while `Selfish` is *not* a subtype of `Person`. In other words, inheritance 515 | expresses that "`Childish` *is* a `Person`" while self-type annotation expresses that "whatever is `Selfish` *must also be* a `Person`". The fact that `Selfish` can be treated as `Person` is only seen inside the body of `Selfish`. 516 | * Because self-type is just a *requirement*, it must ultimately be actually inherited by some class or trait or repeated by a trait or abstract class. This introduces some boilerplate to maintain. Changing self-type to another or more specific type may require you to adjust many subtraits or subclasses of the trait with self-type annotation. This is typically not necessary when using inheritance. 517 | * Self-type annotations can express things that are impossible to express with inheritance, e.g. 518 | ```scala 519 | // a cycle! 520 | trait A { this: B => } 521 | trait B { this: A => } 522 | // self-type may be arbitrary type that can possibly refer to type parameters 523 | trait Self[T] { this: T => } // referring to generic in self-type 524 | trait ObjectBase { this: SomeObject.type => } // trait `ObjectBase` may only be extended by `object SomeObject` 525 | ``` 526 | 527 | #### `this` alias 528 | 529 | Self-type annotation may also introduce an alias identifier for `this`. This is useful especially when your trait/class has inner classes which need to refer to outer instance. 530 | 531 | ```scala 532 | trait Logging { 533 | def log(msg: String): Unit 534 | } 535 | trait Handler { handler: Logging => 536 | def handle(stuff: String): Unit = 537 | new Thread { 538 | def run(): Unit = { 539 | handler.log(s"Handling $stuff") // using `handler` instead of `Handler.this` 540 | } 541 | }.start() 542 | } 543 | ``` 544 | 545 | When self-type annotation is used only for that purpose, the actual self-type may be omitted and only the alias is left: 546 | 547 | ```scala 548 | trait Handler { handler => 549 | // code that uses `handler` 550 | } 551 | ``` 552 | -------------------------------------------------------------------------------- /chapters/Type-system-basics.md: -------------------------------------------------------------------------------- 1 | # Type system basics 2 | 3 | ## Unification of types 4 | 5 | **Java** type system divides its types into two groups: *objects*, where every value is an instance of a class that extends from `java.lang.Object` and *primitives* like `int`, `double`, `boolean`, etc. Primitives are treated specially by the compiler. They can be used as types of local variables, fields, method parameters and return values, but do not participate in class hierarchy and and their values are not *objects*. In particular, they cannot be used as type parameters (generics). 6 | 7 | Scala is different. It's a purely object oriented language in the sense that every value is an object. That is, all the types have their classes and participate in a single class hierarchy. An outline of this hierarchy is shown below: 8 | 9 | ![Scala type hierarchy](../images/scalatypes.png) 10 | 11 | #### `Any` 12 | 13 | At the top of entire hierarchy sits the type `Any`. Every single value in Scala is an instance of `Any`. This special class exposes some universal methods and operators available on any value: `==`, `!=`, `equals`, `hashCode`, `toString`, `getClass` and a few others. As you can see, Scala moves some of the methods normally available on `java.lang.Object` to an even more common class. 14 | 15 | #### `AnyVal` 16 | 17 | `AnyVal` is a direct subtype of `Any`. It does not have any methods by itself, but serves only as a common superclass for the so-called *value* types. Value types include mostly those which correspond to Java primitives - `Unit`, `Boolean`, `Char`, `Byte`, `Short`, `Int`, `Long`, `Float` and `Double`. However, they are different from Java primitives since they are actual Scala *classes*. For example, they can be used as type parameters (e.g. you can have `List[Int]`). However, it is not possible to assign `null` to value types. 18 | 19 | Scala primitive classes are largely interoperable with Java primitives: 20 | * Scala tries to represent primitive classes as actual Java primitives in runtime. However, this is not guaranteed. For example, if you create a `java.util.List[Int]`, elements of that list will be actually represented in runtime as `java.lang.Integer` objects. 21 | * Scala methods accepting primitive parameters or returning them are seen by Java as methods accepting and returning Java primitives and vice versa, i.e. the two following signatures are seen as identical when consumed by both Scala and Java type system: 22 | 23 | ```scala 24 | def stuff(i: Int, d: Double): Long 25 | ``` 26 | ```java 27 | public long stuff(int i, double d); 28 | ``` 29 | * Scala defines the same implicit conversions between primitive types as Java does - these are marked on the diagram with dashed arrows. It also maintains implicit conversions between primitives and their boxed counterparts. For example, you can pass `Int` whenever a `java.lang.Integer` is expected and vice versa. These conversions are not shown on the diagram. 30 | 31 | Apart from counterparts of Java primitives, it is also possible to define custom classes that extend `AnyVal`. These are so called *value classes* and are more advanced concept that will not be covered here. 32 | 33 | #### `AnyRef` 34 | 35 | `AnyRef` is simply an alias for `java.lang.Object`. It is the common superclass of all *reference* or *object* types. Entire hierarchy of Java classes sits here. By default, all Scala classes, traits and objects are also subtypes of `AnyRef`. 36 | 37 | Apart from all the standard Java methods available for `java.lang.Object`, Scala type system adds reference equality operators to the `AnyRef` class - these are the `eq` and `ne` operators mentioned in previous chapter. It also adds an artificial `synchronized` method to it, which is a replacement for language-native `synchronized` keyword in Java. For example, the Java snippet: 38 | 39 | ```java 40 | synchronized(someObject) { 41 | // some code here 42 | } 43 | ``` 44 | 45 | would be rewritten to Scala as: 46 | 47 | ```scala 48 | someObject.synchronized { 49 | // some code here 50 | } 51 | ``` 52 | 53 | Scala also makes `synchronized` an expression which can return a value. E.g. we can compute some value under lock and return it: 54 | 55 | ```scala 56 | val someInt = someObject.synchronized { 57 | // some computation that needs synchronization 58 | 42 // returned value 59 | } 60 | ``` 61 | 62 | Classes extending `AnyRef` are nullable. 63 | 64 | #### `Null` 65 | 66 | `Null` is a special Scala type which has only one value - `null`. It is also a subtype of all reference (object) types, which expresses the fact that you can assign `null` to them. 67 | 68 | #### `Nothing` 69 | 70 | `Nothing` sits at the very bottom of Scala type hierarchy. It is a subtype of all other types. `Nothing`, as the name suggests, has literally *no values*. If something (i.e. a method) has return type declared as `Nothing`, it is guaranteed to throw an exception in runtime (`Throwable`, to be precise). The most common usage of `Nothing` is to express empty collections. For example, `Nil` - an object which represents empty list in Scala has type `List[Nothing]`. Thanks to that and the fact that `List` is *covariant*, `Nil` can be used as empty list of any element type. This will be all covered in a separate chapter, along with explanation about what this *covariant* thing means. 71 | 72 | `Nothing` is sometimes also used as a return type of methods which exists only in compile time, e.g. are elided by *macros*. Don't worry about these now, though - we just mention it for the sake of completeness. 73 | 74 | #### Universal traits 75 | 76 | It is possible to define some types that directly extend `Any`. These are related to *value classes* and will be explained along with them, in some other (more advanced) chapter. 77 | 78 | ### Summary 79 | 80 | As you can see, Scala largely unifies primitive and reference types by giving them a common hierarchy and allowing to use primitives where they could not be normally used in Java. At the same time, Scala still maintains some distinction between primitives and objects. 81 | 82 | ### Casting 83 | 84 | In Java, there is only one syntax for casting - it is the C-style `(ForcedType)castValue` syntax, but we differentiate between: 85 | * *upcasting* - when we force a value of some class to be treated as value of its superclass. This is a perfectly safe cast. It is usually needed for forcing some particular overloaded variant of a method to be used. 86 | * *downcasting* - when we force a value of some class to be treated as value of its subclass. This is an unsafe cast that will result in `ClassCastException` being thrown in runtime if the actual class is not what we asked for. 87 | 88 | #### Type ascription 89 | 90 | In Scala, you can perform upcasting using the *type ascription* syntax: 91 | 92 | ```scala 93 | def overloaded(any: Any): Unit 94 | def overloaded(str: String): Unit 95 | overloaded("treatMeAsAny": Any) 96 | ``` 97 | 98 | Type ascription is much more useful than just for overloading resolution, because it can guide Scala's type inference: 99 | 100 | ```scala 101 | val x = "treatMeAsAny": Any 102 | ``` 103 | 104 | The snippet above is equivalent to: 105 | 106 | ```scala 107 | val x: Any = "treatMeAsAny" 108 | ``` 109 | 110 | but has a slightly different meaning from the point of view of the type system. The latter means "declare value `x` of type `Any` and assign it a string value" while the former means "declare value `x` and assign it a string value explicitly treated as `Any`". The difference is that the first syntax uses type inference - we don't declare the type of `x` but let the compiler infer it from the assigned value. However, the value we assign is a string up-cast to `Any`, so the type of `x` will also be inferred as `Any`. 111 | 112 | Type ascription can also be used to force usage of implicit conversion, e.g. `42: java.lang.Integer` will force 42 - a value of type `Int` - to be converted to `java.lang.Integer`. Note however that the static type of such expression will also be `java.lang.Integer` so this is more than just forcing a boxed representation in runtime. 113 | 114 | #### Unsafe casts 115 | 116 | Downcasting and other unsafe casts can be performed in Scala with the `asInstanceOf` artificial method, i.e. `value.asInstanceOf[Type]`. Of course, this is unsafe so the only situations where this is justified is when you need to work with completely untyped data (i.e. deserialization) or some untyped external APIs. Sometimes it is also needed to work around limitations of the type system. 117 | 118 | Scala also provides the `isInstanceOf` method which is an equivalent of Java `instanceof` keyword. Of course, you should avoid it in the same way as for `asInstanceOf`. 119 | 120 | Unsafe casting can also be done with the pattern matching syntax: 121 | 122 | ```scala 123 | someObj match { 124 | case str: String => println(str.substring(1)) 125 | case _ => println("Not a string") 126 | } 127 | ``` 128 | 129 | Pattern matching will do both runtime type checking and type casting at the same time. Of course, this is not any less unsafe than `isInstanceOf` and `asInstanceOf`, even if it has nicer syntax. We will cover pattern matching in more detail later. 130 | 131 | As a somewhat related detail, class objects in Scala can be accessed using the `classOf` syntax, i.e. `classOf[String]` in Scala is equivalent to `String.class` in Java. 132 | 133 | ### Handling untyped Java APIs 134 | 135 | Due to the fact that hierarchy of toplevel types in Scala is a bit more complicated than in Java by having distinction between `Any` and `AnyRef`, we may sometimes run into problems when using some untyped Java APIs. In most simple cases this is not a problem - Scala compiler conveniently sees methods like this one: 136 | 137 | ```java 138 | public Object doSomething(Object anything); 139 | ``` 140 | 141 | as: 142 | 143 | ```scala 144 | def doSomething(anything: Any): Object 145 | ``` 146 | 147 | As you can see, the Scala compiler interprets parameters with type `java.lang.Object` in Java methods as parameters with type `Any`. This is nice because we can pass value types to these methods without explicit conversions to their boxed representations. 148 | 149 | But you may wonder - how can this be correct? `java.lang.Object`, or `AnyRef` is more specific than `Any` - isn't the compiler lying to us about the signatures? Technically, it does, but the truth is that in runtime, values of static type `Any` will need to be boxed anyway which means that they can safely be passed where `java.lang.Object` is required. When someone uses `java.lang.Object` in Java API, it usually means "anything" so it makes sense to assume that if this API was written in Scala, there would be `Any` in the signature and not `AnyRef`. 150 | 151 | However, things get a bit more complex when method signatures are no longer that simple: 152 | 153 | Imagine we have a (completely untyped and ugly) Java API for saving some arbitrary value to database. The value is represented as `java.util.Map`: 154 | 155 | ```java 156 | public void save(java.util.Map fieldsWithValues); 157 | ``` 158 | 159 | This will be seen by Scala as: 160 | 161 | ```scala 162 | def save(fieldsWithValues: java.util.Map[String,AnyRef]): Unit 163 | ``` 164 | 165 | Let's assume we want to put ints as values into our map: 166 | 167 | ```scala 168 | val map = new java.util.HashMap[String,AnyRef] 169 | map.put("something", 42) // error: the result type of an implicit conversion must be more specific than AnyRef 170 | ``` 171 | 172 | The above snippet will not compile, because `42` is of type `Int` and it cannot be passed where `AnyRef` is expected. We are forced to request a conversion to `java.lang.Integer`: 173 | 174 | ```scala 175 | map.put("something", 42: java.lang.Integer) 176 | ``` 177 | 178 | We would prefer to use `Any` instead of `AnyRef` so that we don't have to convert explicitly, but then we won't be able to pass our map to the Java API, which expects `java.util.Map[String,AnyRef]`: 179 | 180 | ```scala 181 | val map = new java.util.HashMap[String,Any] 182 | map.put("something", 42) 183 | save(map) // error 184 | ``` 185 | 186 | However, we can employ an ugly hack and leverage the fact that if something is declared as `Any`, it will *always* be boxed in runtime. Since `Any` is effectively represented as `AnyRef` in runtime, we can do an ugly but perfectly safe cast: 187 | 188 | ```scala 189 | save(map.asInstanceOf[java.util.Map[String,AnyRef]]) 190 | ``` 191 | 192 | In fact, it turns out that `x.asInstanceOf[AnyRef]` *always* works, no matter what `x` is. Casting to `AnyRef` simply forces a boxed representation. So for example `42.asInstanceOf[AnyRef]` will not fail but force the int to be represented as `java.lang.Integer`. We can use this trick to fool the Scala type system and retain slightly nicer API on Scala side (with `Any` instead of `AnyRef`). 193 | 194 | As a somewhat related fact, there's another trick with `asInstanceOf`: it turns out that `null.asInstanceOf[T]` also always works (except for when `T` is `Nothing`). This is because `null` is a valid representation for primitive types and will be understood as "zero" of given type. For example, `null.asInstanceOf[Int]` will evaluate as 0. 195 | 196 | All of the stuff described above is of course a dark, dirty corner of the language and its implementation, but may be needed and even make your Scala APIs better sometimes. That's why we decided to mention it here. 197 | 198 | #### Summary 199 | 200 | The morale of the story is: if you have some untyped value in your Scala code, always represent it with `Any`. Even if some legacy Java API expects `java.lang.Object`, you can always safely cast. 201 | -------------------------------------------------------------------------------- /chapters/Variance.md: -------------------------------------------------------------------------------- 1 | Variance is a problem that appears in the area of interaction between generics/type members and subtyping. 2 | 3 | Suppose that we have a generic class `Box` along with some simple class hierarchy: 4 | 5 | ```scala 6 | class Box[T](var value: T) 7 | 8 | abstract class Animal 9 | class Frog extends Animal 10 | abstract class Mammal extends Animal 11 | class Cat extends Mammal 12 | class Dog extends Mammal 13 | ``` 14 | 15 | Now, can we do this? 16 | 17 | ```scala 18 | val mammalBox: Box[Mammal] = new Box[Mammal](new Dog) 19 | val animalBox: Box[Animal] = mammalBox //error! 20 | ``` 21 | 22 | The answer is no, `scalac` will give us an error and it will be right about it, because otherwise we could do this: 23 | 24 | ```scala 25 | animalBox.value = new Frog // putting a frog into a box only for mammals :( 26 | ``` 27 | 28 | which would be totally wrong, because we expect that `mammalBox` contains only mammals and we've just put a frog into it. Sooner or later we're going to see a `ClassCastException` when trying to access the `value` of `mammalBox`. 29 | 30 | But what if our intention was not to put `value` into `animalBox` but only accessing it? Then it would be totally safe, because `Mammal` is a subtype of `Animal`, so without any problem we could do: 31 | 32 | ```scala 33 | val animal: Animal = animalBox.value 34 | ``` 35 | 36 | Maybe there's some way to tell the compiler that we only want to use the "safe" portion of `Box` API? 37 | Actually, we could do it using wildcards: 38 | 39 | ```scala 40 | val mammalBox: Box[Mammal] = new Box[Mammal](new Dog) 41 | val animalBox: Box[_ <: Animal] = mammalBox 42 | ``` 43 | 44 | Now the compiler knows that `animalBox` is not a box for an animal but a box for something that is an animal but may be more specific. Now it has just enough information to know that getting an animal from the box is fine, but putting an animal into it is not: 45 | 46 | ```scala 47 | val animal: Animal = animalBox.value 48 | animalBox.value = (new Frog): Animal //error! 49 | ``` 50 | 51 | Now, let's consider a similar problem where we're interested into putting things into a box but not interested in accessing it. For example: 52 | 53 | ```scala 54 | val mammalBox: Box[Mammal] = new Box[Mammal](new Dog) 55 | val catBox: Box[Cat] = mammalBox //error! 56 | catBox.value = new Cat 57 | ``` 58 | 59 | Scala will, again, refuse to compile the line where `mammalBox` is cast to `Box[Cat]`. And it's right to do so, because accessing `catBox.value` should give us a `Cat` and we know that there's a `Dog` instead. So we would see `ClassCastException` again. In order to tell the compiler that we're only interested in putting cats into `catBox`, we can use another wildcard: 60 | 61 | ```scala 62 | val catBox: Box[_ >: Cat] = mammalBox 63 | ``` 64 | 65 | Now the compiler knows that `catBox` is a box not just for cats but for some more general, unknown class of animals. This way it will allow us to put a cat into this box, but won't let us assume that there's always a cat in it. 66 | 67 | ### What goes in and out 68 | 69 | When a class has a type parameter, looking into its usages inside this class we can determine whether the generic value goes "in" or "out" of the class (or both or neither). In the case of `Box[T]` the `T` goes both "out" and "in" because we can both access and modify the `value` field. 70 | But if `Box` was immutable, the parameter `T` would only go "out", e.g. 71 | 72 | ```scala 73 | class ImmutableBox[T](val value: T) 74 | ``` 75 | 76 | On other occasions, a generic may only go "in", e.g. 77 | 78 | ```scala 79 | trait Consumer[T] { 80 | def consume(value: T): Unit 81 | } 82 | ``` 83 | 84 | By using wildcards, we are telling the compiler that we want to use only the part of an API of a class where a type parameter goes "out" (e.g. `Box[_ <: Animal]`) or only the part where it goes "in" (e.g. `Box[_ >: Cat]`). 85 | 86 | ## Usage site variance 87 | 88 | This usage of wildcards is known as *usage site variance*. In the first case ("out", `Box[_ <: Animal]`) we're talking about *covariance* and in the second one ("in", `Box[_ >: Cat]`), it's *contravariance*. 89 | They're *usage-site* because wildcards need to be repeated at every usage of `Box` type to indicate covariance or contravariance. This is as opposed to *declaration site variance* which we'll talk about later. 90 | 91 | When a type parameter is used in a position where it goes "out" of its class, then we call it a *covariant position*. Conversely, when it's used in a position where it goes "in", we call it a *contravariant position*. And finally, when it's used in a position where it could go both "out" and "in", we call it an *invariant position*. 92 | 93 | ```scala 94 | class VarianceBox[T] { 95 | def value: T = rawValue // covariant position, T goes "out" 96 | def setValue(value: T): Unit = { // contravariant position, T goes "in" 97 | rawValue = value 98 | } 99 | var rawValue: T // invariant position (or technically a covariant getter with a contravariant setter, independently) 100 | } 101 | ``` 102 | 103 | **NOTE**: when type parameter appears in a constructor parameter, we do **NOT** consider it a contravariant ("in") position, because the value only goes "in" during construction when the exact type of the object is known. Such positions are actually *bivariant* (neither "in" nor "out"). 104 | 105 | Now we can set out the rules about usage-site variance in a more formal way: 106 | * *upper-bounded* wildcards (e.g. `Box[_ <: Animal]`) let us only use the portion of an API where type parameter appears in *covariant* positions 107 | * *lower-bounded* wildcards (e.g. `Box[_ >: Cat]`) let us only use the portion of an API where type parameter appears in *contravariant* positions 108 | 109 | In exchange for the limitations imposed by wildcards, we can establish a subtyping relation between otherwise unrelated applications of the outer, parametric class. For example: 110 | 111 | * because `Mammal <: Animal` (`<:` = *is-a-subtype-of*), we can say that `Box[Mammal] <: Box[_ <: Animal]` 112 | * because `Mammal >: Cat` (`>:` = *is-a-supertype-of*), we can say that `Box[Mammal] >: Box[_ >: Cat]` 113 | 114 | ## Declaration site variance 115 | 116 | You may have noticed that on many occasions a type parameter either *only* goes "out" or *only* goes "in". For example, all the immutable collections in Scala allow only retrieval of data and therefore the element type parameter appears only in covariant positions (e.g `T` in `List[T]`). In such cases Scala allows us to explicitly mark such type parameter as covariant or contravariant, at the place of its declaration. 117 | 118 | In order to declare a type parameter *covariant* ("out"), we use the `+` symbol: 119 | 120 | ```scala 121 | class ImmutableBox[+T](val value: T) 122 | ``` 123 | 124 | Thanks to this, we no longer need to use wildcards in usage site. Now we can simply say: 125 | 126 | ```scala 127 | val mammalBox: ImmutableBox[Mammal] = new ImmutableBox(new Cat) 128 | val animalBox: ImmutableBox[Animal] = mammalBox // no need to use wildcards now! 129 | ``` 130 | 131 | At the same time, the compiler now knows that it must ensure that `T` is used in `ImmutableBox` class only in covariant positions. If it's not, we'll get a compilation error. 132 | 133 | Similarly, in order to declare a type parameter *contravariant* ("in"), we use the `-` symbol: 134 | 135 | ```scala 136 | trait Hole[-T] { 137 | def put(value: T): Unit 138 | } 139 | ``` 140 | 141 | Which allows us to omit lower-bounded wildcards at usage site: 142 | 143 | ```scala 144 | val mammalHole: Hole[Mammal] = ??? 145 | val catHole: Hole[Cat] = mammalHole // no need to use wildcards now! 146 | ``` 147 | 148 | In the same manner, the compiler now checks if `T` only appears at contravariant positions in `Hole`. 149 | 150 | ### Common uses and examples of variance 151 | 152 | The most common situation where a type parameter is declared as covariant are all sorts of immutable data structures like `Option` or `List`. Covariance is natural for them because since they're immutable, data can only be retrieved from them and not mutated inside them. Therefore, the type-parameterized data only goes "out" of them. If you look at various collections in the standard library, you'll see that almost all immutable collection implementations (e.g. `List`, `Vector`) and non-mutable collection traits (e.g. `scala.collection.Traversable`, `scala.collection.Seq`) have their element type parameter declared as covariant. A controversial exception is `Set` which isn't covariant because of its `contains` and `apply` methods (a `Set` can be seen as a predicate, i.e. function from its element type to `Boolean`). 153 | 154 | **NOTE**: we have made a distinction between _immutable_ and _non-mutable_ collections. The word _immutable_ refers to collection traits and classes which are guaranteed to never change their contents. These usually inhabit the `scala.collection.immutable` package. However, there are also traits in the `scala.collection` package which are base traits for both immutable and mutable collections. To these we refer as _non-mutable_ because while they may be extended by mutable collection implementations, by themselves they don't expose any mutating API. That alone is enough in order to make them covariant. -------------------------------------------------------------------------------- /images/scalatypes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghik/opinionated-scala/57f2a276f39f9d9c638cfedfd85e77ef352ef0a6/images/scalatypes.png --------------------------------------------------------------------------------