Functions
A function is basically a method, but we can use a function as a first-class value:
- we can pass it as an argument or parameter to a method or function;
- we can return it from a method or function; and
- we can give it a name using
val
.
Here's an example where we give the name add42
to a function that adds 42 to its input.
val add42 = (x: Int) => x + 42
// add42: Function1[Int, Int] = repl.MdocSession$MdocApp$$Lambda$13883/0x0000000103a92040@35b5b6a1
We can call it just like we'd call a method.
add42(0)
// res0: Int = 42
This is an example of a function literal. Let's learn about them now.
Function Literals
We've just seen an example of a function literal, which was
(x: Int) => x + 42
// res1: Function1[Int, Int] = repl.MdocSession$MdocApp$$Lambda$13884/0x0000000103a76040@2e921698
The general syntax is an extension of this.
<div class="callout callout-info">
Function Literal Syntax {-}
The syntax for declaring a function literal is
(parameter: type, ...) => expression
where
- the optional parameter
s are the names given to the function parameters;
- the type
s are the types of the function parameters; and
- the expression
determines the result of the function.
The parentheses around the parameters are optional if the function has just a single parameter. </div>
Function Types
To pass functions to methods we need to know how to write down their types (because when we declare a parameter we have to declare its type).
We write a function type like (A, B) => C
where A
and B
are the types of the parameters and C
is the result type.
The same pattern generalises from functions of no arguments to an arbitrary number of arguments.
Here's an example. We create a method that accepts a function, and that function is from Int
to Int
. We write this type as Int => Int
or (Int) => Int
.
def squareF(x: Int, f: Int => Int): Int =
f(x) * f(x)
We can pass add42
to this method
squareF(0, add42)
// res2: Int = 1764
We could also pass a function literal
squareF(0, x => x + 42)
// res3: Int = 1764
Note that we didn't have to put the parameter type on the function literal in this case because Scala has enough information to infer the type.
<div class="callout callout-info">
Function Type Declaration Syntax {-}
To declare a function type, write
(A, B, ...) => C
where
A, B, ...
are the types of the input parameters; andC
is the type of the result.
If a function only has one parameter the parentheses may be dropped:
A => B
</div>
Functions as Objects
All first class values are objects in Scala, including functions. This means functions can have methods, including some useful means for composition.
val addTen = (a: Int) => a + 10
// addTen: Function1[Int, Int] = repl.MdocSession$MdocApp$$Lambda$13886/0x0000000103a90840@1b29e778
val double = (a: Int) => a * 2
// double: Function1[Int, Int] = repl.MdocSession$MdocApp$$Lambda$13887/0x0000000103a88040@72e8ce3
val combined = addTen.andThen(double) // this composes the two functions
// combined: Function1[Int, Int] = scala.Function1$$Lambda$13817/0x0000000103a70040@1f362219 // this composes the two functions
combined(5)
// res4: Int = 30
Calling a function is actually calling the method called apply
on the function. Scala allows a shortcut for any object that has a method called apply
, where can drop the method name apply
and write the call like a function call. This means the following are equivalent.
val halve = (a: Int) => a / 2
// halve: Function1[Int, Int] = repl.MdocSession$MdocApp$$Lambda$13888/0x0000000103a8f040@21aec1b1
halve(4)
// res5: Int = 2
halve.apply(4)
// res6: Int = 2
Converting Methods to Functions
Methods are very similar to functions, so Scala provides a way to convert functions to methods. If we follow a method name with a _
it will be converted to a function.
def times42(x: Int): Int =
x * 42
val times42Function = times42 _
// times42Function: Function1[Int, Int] = repl.MdocSession$MdocApp$$Lambda$13889/0x0000000103a9f040@7568f0db
We can also write a method call but replace all parameters with _
and Scala will convert the method to a function.
val times42Function2 = times42(_)
// times42Function2: Function1[Int, Int] = repl.MdocSession$MdocApp$$Lambda$13890/0x0000000103a9e840@6d23301
Exercises {-}
Function Literals
Let's get some practice writing function literals. Write a function literal that:
- squares it's
Int
input; - has a
Color
parameter and spins the hue of thatColor
by 15 degrees; and - takes an
Image
input and creates four copies in a row, where each copy is rotated by 90 degrees relative to the previous image (use therotate
method onImage
to achieve this.)
<div class="solution"> The first function is
(x: Int) => x * x
// res7: Function1[Int, Int] = repl.MdocSession$MdocApp$$Lambda$13891/0x0000000103a9d840@28daff28
The second is
(c: Color) => c.spin(15.degrees)
// res8: Function1[Color, HSLA] = repl.MdocSession$MdocApp$$Lambda$13892/0x0000000103a8e840@7f2e10cb
The third is
(image: Image) =>
image.beside(image.rotate(90.degrees))
.beside(image.rotate(180.degrees))
.beside(image.rotate(270.degrees))
.beside(image.rotate(360.degrees))
// res9: Function1[Image, Image] = repl.MdocSession$MdocApp$$Lambda$13893/0x0000000103a8d840@4d72d9dd
</div>
Function Types {-}
Here's an interesting function we'll do more with in later sections. We don't need to understand what it does right now, though you might want to experiment with it.
val roseFn = (angle: Angle) =>
Point.cartesian((angle * 7).cos * angle.cos, (angle * 7).cos * angle.sin)
What is the type of the function roseFn
defined above? What does this type mean?
<div class="solution">
The type is Angle => Point
. This means roseFn
is a function that takes a single argument of type Angle
and returns a value of type Point
. In other words, roseFn
transforms an Angle
to a Point
.
</div>