Published 2015-02-05.
Last modified 2015-09-13.
Time to read: 5 minutes.
Anonymous functions are also known as lambdas. It takes effort to become comfortable with using lambdas in their various forms and scenarios, but this skill is essential in order to work with Scala effectively. This lecture is intended to give you a summary of the material presented so far, followed by a drill.
As always, I recommend that you follow along.
Please try out every code example in the REPL.

Review
A lambda that accepts one parameter of type SomeType
and returns an instance of AnotherType
is an instance of a Function1
, and is of the form:
(x: SomeType) => x.methodInvocation
You can assign a lambda to a variable:
val fn = (x: SomeType) => x.methodInvocation
Let’s work with an example where SomeType
is String
and AnotherType
is Int
.
scala> val fn = (x: String) => x.length fn: String => Int = <function1>
We can invoke fn
by passing it the value "hi there
" for the parameter x
like this.
scala> fn("hi there") res1: Int = 8
Note that the type of fn
is String => Int
, which is shorthand for Function1[String, Int]
.
ScalaDoc uses this shorthand.
The above has no declared type; the type is inferred by the Scala compiler.
Careful, do not forget to provide parentheses around the declaration of the x
parameter:
scala> val fn = x: String => x.length <console>:7: error: not found: value x val fn = x: String => x.length ^
We could explicitly declare the type of fn
with this equivalent expression:
scala> val fn: (String) => Int = (x: String) => x.length fn: String => Int = <function1>
The resulting Function1
definition is identical and of course the returned value is the same as before.
scala> fn("hi there") res1: Int = 8
When there is only one argument to the function the parentheses around the parameter in the type declaration are optional.
We can remove the parentheses around the parameter in the type declaration for a Function1
, like this.
scala> val fn: String => Int = (x: String) => x.length fn: String => Int = <function1>
The resulting Function1
definition is identical to the previous lambda definition,
and of course the returned value from invoking the lambda is the same as before.
scala> fn("hi there") res1: Int = 8
Here is the identical definition, but written in long form:
scala> val fn: Function1[String, Int] = (x: String) => x.length fn: String => Int = <function1>
You can invoke this version to verify that it works just the same:
scala> fn("hi there") res2: Int = 8
There is redundancy in the above definition for n
because the type of the parameter (String
) is mentioned twice.
You could shorten the above because the type of fn
is declared on the left hand side of the equals sign,
which tells the Scala compiler that the type of the variable x
is String
,
and that the return value is Int
.
scala> val fn: String => Int = x => x.length fn: String => Int = <function1>
Here is the identical definition, written in long form:
scala> val fn: Function1[String, Int] = x => x.length fn: String => Int = <function1>
Notice that x
is mentioned twice, but only used once.
This is redundant.
When you have a lambda that only references a parameter once you can use placeholder syntax to shorten the definition even further.
In this case the underscore means "the variable passed in as an argument which has the type as specified elsewhere".
In this case the type is specified on the left-hand side of the equals sign.
scala> val fn: Function1[String, Int] = _.length fn: String => Int = <function1>
Of course you could also declare the type using shorthand for an identical result:
scala> val fn: String => Int = _.length fn: String => Int = <function1>
You could also write this with the type information on the right-hand side of the equals sign using an alternative form of placeholder syntax for lambdas.
scala> val fn = (_: String).length fn: String => Int = <function1>
scala> fn("hi there") res5: Int = 8
As you will learn in the Higher-Order Functions
lecture of the Intermediate Scala course,
when you have a higher-order function, the type of the function that must be passed into the higher-order function
is declared as part of the method signature of the higher-order function.
Thus the compiler knows the method signature required for Function
s passed to the higher-order function as an argument.
Next we see a higher-order function that accepts two arguments: a String
and a
Function1[String, Int]
called fn
.
We can pass in any of the definitions for fn
above when we invoke the method.
scala> def myHigherOrderFunction(string: String, fn: String => Int): Int = fn(string) myHigherOrderFunction: (string: String, fn: String => Int)Int
scala> myHigherOrderFunction("blah", fn) res4: Int = 4
Drills

Drill 1
Which of these define the same Function1
?
-
Scala code 1
val fn1 = (x: Int) => x * 2
-
Scala code 2
val fn2: Int => Int = _ * 2
-
Scala code 3
val fn3: Function1[Int, Int] = x => x * 2
-
Scala code 4
val fn4 = (_:Int) * 2
Solution 1
All of them.
scala> val fn1 = (x: Int) => x * 2 fn: Int => Int = <function1>
scala> fn1(21) res0: Int = 42
scala> val fn2: Int => Int = _ * 2 fn: Int => Int = <function1>
scala> fn2(21) res1: Int = 42
scala> val fn3: Function1[Int, Int] = x => x * 2 fn: Int => Int = <function1>
scala> fn3(21) res2: Int = 42
scala> val fn4 = (_:Int) * 2 fn: Int => Int = <function1>
scala> fn4(21) res3: Int = 42
Drill 2
What is the equivalent long-form type declaration for this lambda?
(i: Int, str: String) => str * i
Function1[Int, String, String]
Function2[Int, String, String]
Int => String
(Int, String) => String
Solution 2
Let’s run the lambda to see what it does:
scala> (i: Int, str: String) => str * i res4: (Int, String) => String = <function2>
scala> res4(3, "hi ") res5: String = "hi hi hi "
The lambda accepts an Int
and a String
, and returns a new String
that repeats the original string i
times.
#1 and #3 are clearly incorrect because a Function1
can only accept one parameter,
and this lambda accepts two parameters.
#2 is correct.
The type of the lambda, expressed in shorthand, is (Int, String) => String
,
and expressed in long form this is Function2[Int, String, String]
.
#4 is the lambda’s actual type as reported by the REPL, but this is expressed in shorthand, not long form as required by the question.
Drill 3
What is the equivalent long-form type declaration for this lambda?
(_: String) * (_: Int)
Function1[String, Int, String]
Function2[String, Int, String]
String => Int
(String, Int) => String
Solution 3
Let’s run the lambda to see what it does:
scala> (_: String) * (_: Int) res5: (Int, String) => String = <function2>
scala> res5("hi ", 3) res5: String = "hi hi hi "
The lambda accepts two unnamed parameters, using placeholder syntax: a String
and an Int
.
The lambda returns a new String
that repeats the original string the number of times specified by the Int
.
The placeholder syntax can only be used when each parameter is only referenced once.
#1 and #3 are clearly incorrect because a Function1
can only accept one parameter, and this lambda accepts two parameters.
#2 is correct.
The type of the lambda, expressed in shorthand, is (String, Int) => String
.
Expressed in long form this is Function2[String, Int, String]
.
#4 is the actual type of the lambda, expressed in shorthand, not long form as required by the question.
Again, note that the reversing the order of the parameters causes a syntax error.
scala> (_: Int) * (_: String) <console>:8: error: overloaded method value * with alternatives: (x: Double)Double <and> (x: Float)Float <and> (x: Long)Long <and> (x: Int)Int <and> (x: Char)Int <and> (x: Short)Int <and> (x: Byte)Int cannot be applied to (String) (_: Int) * (_: String) ^
This is because the * method is bound to the first parameter, as if you wrote:
(_: Int).*((_: String))
Int
has no method called * that accepts a String
, hence the compiler error.
Drill 4
Which of these define the same Function2
?
-
Scala code 1
val fn1 = (str: String, i: Int) => str * i
-
Scala code 2
val fn2: (String, Int) => String = _ * _
-
Scala code 3
val fn3: Function2[String, Int, String] = (x, y) => x * y
-
Scala code 4
val fn4 = (_: String) * (_:Int)
Solution 4
All of them.
scala> val fn1 = (str: String, i: Int) => str * i fn1: (String, Int) => String = <function2>
scala> fn1("hi ", 3) res11: String = "hi hi hi "
scala> val fn2: (String, Int) => String = _ * _ fn2: (String, Int) => String = <function2>
scala> fn2("hi ", 3) res12: String = "hi hi hi "
scala> val fn3: Function2[String, Int, String] = (x, y) => x * y fn3: (String, Int) => String = <function2>
scala> fn3("hi ", 3) res13: String = "hi hi hi "
scala> val fn4 = (_: String) * (_:Int) fn4: (String, Int) => String = <function2>
scala> fn4("hi ", 3) res14: String = "hi hi hi "
Drill 5
Which are shorthand equivalents for the following long form FunctionN
type?
Function3[String, Int, Int, Boolean]
(String) => (Int) => (Int) => (Boolean)
(String, Int, Int, Boolean)
(String, Int, Int) => Boolean
(String) => Boolean
Solution 5
#1 is an example of a curried function, which we will discuss in the Partially Applied Functions lecture of the Intermediate Scala course.
#2 does not define a FunctionN
because no rocket operator (=>
) was provided.
#3 is correct. Here is an example:
scala> val fn: (String, Int, Int) => Boolean = (s, i, j) => (s*i).length == j fn: (String, Int, Int) => Boolean = <function3>
scala> fn("abc", 5, 15) res1: Boolean = true
#4 defines a Function1
, not a Function3
.
Exercise - Spark
The Spark documentation shows this method signature.
def subgraph( epred: EdgeTriplet[VD,ED] => Boolean = (x => true), vpred: (VertexId, VD) => Boolean = ((v, d) => true) ): Graph[VD, ED]
The documentation actually has an error, in that VertexId
was mis-spelled as VertexID
.
I’ve corrected that here.
- Explain the type of
epred
. - Where does
x
come from? - What does
(x => true)
do?
Solution
The subgraph method accepts two parameters: epred
and vpred
.
epred
is declared to have type EdgeTriplet[VD,ED] => Boolean
,
which means that it accepts something of type EdgeTriplet[VD,ED]
and returns a Boolean
.
It also has a default value: x => true
.
There was no need to enclose the default value in parentheses, and I would discourage that.
x => true
is a lambda function and must be of type EdgeTriplet[VD,ED] => Boolean
.
Thus x
must be of type EdgeTriplet[VD,ED]
and true
is of course type Boolean
.
The value of x
will be obtained from epred
when subgraph
is invoked.
I think the intent would have been clearer if an underscore was used instead of a named variable,
since the value of x
is discarded.
Here is how I would have written the method signature:
def subgraph1( epred: EdgeTriplet[VD,ED] => Boolean = _ => true, vpred: (VertexId, VD) => Boolean = (_, _) => true ): Graph[VD, ED] = ???
Here is another way to write the method signature, which is more explicit about types:
def subgraph2( epred: EdgeTriplet[VD,ED] => Boolean = _ => true, vpred: (VertexId, VD) => Boolean = (_: (VertexId, VD)) => true ): Graph[VD, ED] = ???
You can play with the code:
$ git clone https://github.com/huangjs/spark-sample-project
$ cd spark-sample-project
$ sbt console
Paste this into the REPL:
import org.apache.spark.graphx._
class X[VD, ED] { def subgraph1( epred: EdgeTriplet[VD,ED] => Boolean = _ => true, vpred: (VertexId, VD) => Boolean = (_, _) => true ): Graph[VD, ED] = ???
def subgraph2( epred: EdgeTriplet[VD,ED] => Boolean = _ => true, vpred: (VertexId, VD) => Boolean = (_: (VertexId, VD)) => true ): Graph[VD, ED] = ??? }
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.