Functions and function values
def with parameters and a return type, defaults and named arguments, a function held in a val, and a lambda handed straight to map.
def, and the signature it carries
def greet(name: String): String = "Hello, " + name names a piece of work, says what it takes and what it gives back, and puts the body after the equals sign. The parameter list is the part in the first brackets and the return type follows the second colon. Writing both is the habit worth forming, because the signature is the documentation a reader trusts first.
A def written inside the body of the object is visible to every line below it, which keeps a small program in one file. The body is a single expression unless you use braces, and the last value inside the braces is the answer. There is no return to write, and adding one is a style the language quietly discourages.
Defaults, and naming the arguments
A parameter can carry a default: def price(base: Int, tax: Int = 2). Call it with one argument and the tax is 2; call it with two and the second wins. That is how a function gets a sensible usual case without a second function standing beside it.
Arguments can also be named at the call: show(value = 7, label = "n"). Named arguments may arrive in any order, and the compiler puts each one where it belongs. Their real use is a call with several values of the same type, where a reader would otherwise have to count positions to work out what is being passed.
Function values, and the lambda
A function can live in a val: val double = (n: Int) => n * 2. The part before the arrow is the parameter, the part after it is the body, and double is now a value you can store in a list or hand to something else. That is the difference from a def: a def is a name for work, a function value is a thing that travels.
Collections take function values directly. nums.map(double) applies the stored one to every item, and nums.map(n => n * n) writes the same idea in place without naming it. That anonymous form is a lambda, and once map has taken one, the rest of the collection methods read the same way.
What happens at a call
A call such as area(3, 4) evaluates the two arguments, gives the parameters those values for the length of the call, and runs the body. Parameters behave like vals inside the body: the name is fixed for that call, and the next call gets a fresh set. A def is not run when the object starts; it is a description of work waiting for a call.
The body is one expression, or a block whose last expression is the answer. That is why there is no return to write: the value of the last line is already the value of the call. A default parameter is used when the argument is missing, and a named argument is matched to its parameter name rather than to its position.
A function value is different: val double = (n: Int) => n * 2 stores the function itself, so it can be passed to map or kept in a list. def names work; a function value is a thing that can travel. Both are called with brackets, which is why the two forms look the same at the call site.
A function that calls itself
A def can call itself, as long as every call moves towards a case that returns without calling again. That case is the base, and without it the calls pile up until the stack runs out — the error names a stack overflow rather than a line in your logic.
For a factorial or a countdown the recursive form is short; for a long list it is not, because each call keeps a frame of its own until the base case is reached.
Watch three things when writing a def: the = before the body (without it the compiler reads a different, older form), the braces around a multi-line body (the last line is the result), and the return type (a body that prints and returns nothing is Unit). If a call gives () on the screen, the function produced no value to keep.
Try these next
- Write
def twice(n: Int): Int = n * 2and call it inside aprintln. - Add a second parameter to
greetwith a default, then call it with one argument and with two. - Store
(n: Int) => n + 1in avaland pass it toList(1, 2, 3).map.
Four signatures and a function value
def greet(name: String): String = "Hello, " + name
def area(w: Int, h: Int): Int = w * h
// The second parameter has a default.
def price(base: Int, tax: Int = 2): Int = base + tax
println(greet("Ada")) // Hello, Ada
println("area: " + area(3, 4)) // area: 12
println("price: " + price(10)) // price: 12
println("price: " + price(10, 5)) // price: 15
// Named arguments, in any order.
def show(label: String, value: Int): String =
label + "=" + value
println(show(value = 7, label = "n")) // n=7
// A function value in a val.
val double = (n: Int) => n * 2
val nums = List(1, 2, 3)
println(nums.map(double)) // List(2, 4, 6)
println(nums.map(n => n * n)) // List(1, 4, 9)A small price list
Run it and follow each call back to the signature above it. Then write a fourth function of your own with a default parameter and call it both ways.
A shipping quote
Run it and trace each printed price back to the function that produced it. Then add a `fragile` parameter with a default to `shipping`, and call it both ways.
A def and a val differ in when the body runs: a def runs on every call, while a val is worked out once, when the object starts. Use def for anything that should be recomputed or that takes parameters.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
In def area(w: Int, h: Int): Int, what does the last Int say?
