Extracts of Programming in Scala
CHAPTERS 7 - 9
- The only control structures are
match, and function calls.
- Almost all of Scala's control structures result in some value.
- Scala adopts the ternary operator model, but calls it
if. In other words, Scala's
if can result in a value.
- Scala also has a
do-while loop. This works like the
while loop except that it tests the condition after the loop body instead of before.
do-while constructs are called "loops", not expressions, because they don't result in an interesting value.
- Whereas in Java, assignment results in the value assigned, in Scala assignment always results in the unit value,
(). Thus, the value of the assignment "
line = readLine()" will always be
() and never be
for (file <- filesHere) println(file) With the "
file <- filesHere" syntax, which is called a generator.
- If you don't want to include the upper bound of the range in the values that are iterated over, use
until instead of
- Sometimes you don't want to iterate through a collection in its entirety; you want to filter it down to some subset. You can do this with a
for expression by adding a filter, an
if clause inside the
- You can include more filters if you want. Just keep adding
- If you add multiple
<- clauses, you will get nested "loops".
- If you prefer, you can use curly braces instead of parentheses to surround the generators and filters. One advantage to using curly braces is that you can leave off some of the semicolons that are needed when you use parentheses because the Scala compiler will not infer semicolons while inside parentheses.
- We can bind the result to a new variable using an equals sign (
=). The bound variable is introduced and used just like a
val, only with the
val keyword left out.
- You can also generate a value to remember for each iteration. To do so, you prefix the body of the
for expression by the keyword
- The syntax for
catch clauses was chosen for its consistency with an important part of Scala: pattern matching.
- You can wrap an expression with a
finally clause if you want to cause some code to execute no matter how the expression terminates.
- The idiomatic way to ensure a non-memory resource, such as file, socket, or database connection, is closed. First you require the resource. Then you start a
try block in which you use the resource. Lastly, you close the resource in a
try-catch-finally results in a value.
try-finally does not result in a value. As in Java, if a
finally clause includes an explicit return statement, or throws an exception, that return value or exception will "overrule" any previous one that originated in the
try block or one of its
- The default case is specified with an underscore (
_), a wildcard symbol frequently used in Scala as a placeholder for a completely unknown value.
- Any kind of constant, as well as other things, can be used in cases in Scala.
match expressions result in a value.
- The simplest approach is to replace every continue by an
if and every break by a
- To use
break, you need to enclose the block with
- Scala allows you to define variables of the same name in nested scopes.
- The most common way to define a function is as a member of some object; such a function is called a method.
- You can define functions inside other functions. Just like local variables, such local functions are visible only in their enclosing block.
- Local functions can access the parameters of their enclosing function.
- Function literals exist in the source code, whereas function values exist as objects at runtime.
- One way to define a function literal more brief is to leave off the parameter types. This is called target typing because the targeted usage of an expression (i.e.,
.filter()) is allowed to influence the typing of that expression (i.e., the type of the
- Sometimes when you use underscores as placeholders for parameters, the compiler might not have enough information to infer missing parameter types. In such cases, you can specify the types using a colon like this:
- Multiple underscores mean multiple parameters, not reuse of a single parameter repeatedly.
someNumbers.foreach(println _) The underscore in this case is not a placeholder for a single argument. It is a placeholder for an entire parameter list.
- A partially applied function is an expression in which you don't supply all of the arguments needed by the function. Instead, you supply some, or none, of the needed arguments. For example, to create a partially applied function expression involving sum, in which you supply none of the three required arguments, you just place an underscore after "sum". The resulting function can then be stored in a variable.
val a = sum _
- Given this code, the Scala compiler instantiates a function value that takes the three integer parameters missing from the partially applied function expression,
sum _, and assigns a reference to that new function value to the variable
a. When you apply three arguments to this new function value, it will return around and invoke sum, passing in those same three arguments:
- A partial function has this name because you are not applying that function to all of its arguments. In the case of
sum _, you are applying it to none of its arguments. But you can also express a partially applied function by supplying only some of the required arguments.
- Since only one argument is missing, the Scala compiler generates a new function class whose
apply method takes one argument.
- If you are writing a partially applied function expression in which you leave off all parameters, such as
println _ or
sum _, you can express it more concisely by leaving off the underscore if a function is required at that point in the code.
(x: Int) => x + more
more is a free variable because the function literal does not itself give a meaning to it. The
x variable, by contrast, is a bound variable because it does have a meaning in the context of the function: it is defined as the function's lone parameter, an
Int. If you try using this function literal by itself, without any more defined in its scope, the compiler will complain.
- The function value (the object) that's created at runtime from this function literal is called a closure.
- A function literal with no free variables, such as
(x: Int) => x + 1, is called a closed term.
- What happens if
more changes after the closure is created? In Scala, the answer is that the closure sees the change.
- Changes made by a closure to a captured variable are visible outside the closure.
- The instance used is the one that was active at the time the closure was created.
- The Scala compiler rearranges things in cases like these so that the captured parameter lives out on the heap, instead of the stack, and thus can outlive the method call that created it. This rearrangement is all taken care of automatically, so you don't have to worry about it. Capture any variable you like:
var, or parameter.
- Scala allows you to indicate that the last parameter to a function may be repeated. This allows clients to pass variable length argument lists to the function. To denote a repeated parameter, place an asterisk after the type of the parameter.
- If you have an array of the appropriate type, and you attempt to pass it as a repeated parameter, you'll get a compiler error. To accomplish this, you'll need to append the array argument with a colon and an
_* symbol, like this:
- Named arguments allow you to pass arguments to a function in a different order. The syntax is simply that each argument is preceded by a parameter name and an equal sign.
- Default parameters are especially helpful when used in combination with named parameters.
- You can turn off tail-call optimization by giving the following argument to the scala shell or to the
- If the recursion is indirect, as in the example of two mutually recursive functions, no optimization is possible.
- A more concise way to define the method, though, is by calling the higher-order function
exists on a passed list, like this:
- Instead of one list of two
Int parameters, you apply this function to two lists of one
Int parameter each.
- Nevertheless, there is a way to get an actual reference to
curriedSum's "second" function. You can use the placeholder notation to use
curriedSum in a partially applied function expression, like this:
- In any method invocation in Scala in which you're passing in exactly one argument, you can opt to use curly braces to surround the argument instead of parentheses.
- Curried definition of
- To make a by-name parameter, you give the parameter a type starting with
=> instead of
- There is no such thing as a by-name variable or a by-name field.
- One difference exists between these two approaches that is important to note. Because the type of
boolAssert's parameter is
Boolean, the expression inside the parentheses in
boolAssert(5 > 3) is evaluated before the call to
boolAssert. The expression
5 > 3 yields true, which is passed to
boolAssert. By contrast, because the type of
byNameAssert's predicate parameter is
=> Boolean, the expression inside the parentheses in
byNameAssert(5 > 3) is not evaluated before the call to
byNameAssert. Instead a function value will be created whose
apply method will evaluate
5 > 3, and this function wil be passed to