I am watching Runar Bjarnason present Functional Programming for Beginners, and at 14:45 he defines a method:
def isDivisibleBy(k: Int): Int => Boolean = i => i % k == 0
and a function:
val isEven = isDivisibleBy(2)
What are the pros and cons of defining isEven
as a function rather than a method?
I have read Scala Functions vs Methods as well as Difference between method and function in Scala, and I understand the semantic differences, but I wonder if there's some deeper reason in this case why a function might or might not be preferable to using a method:
def isEven = isDivisibleBy(2)
Under the hood, there are other differences between functions and methods. Generally, a plain method generated less overhead than a function (which technically is an object with an apply
method).
However, if you try not to care about those differences and think of def
, val
and var
as fields with different semantics, then it’s simply that def
evaluates every time it gets called while val
evaluates only once.
So, a val isEven = isDivisibleBy(2)
should call isDivisibleBy(2)
during its definition and assign the result of isDivisibleBy(2)
. E.g. it replaces the k
in
def isDivisibleBy(k: Int): Int => Boolean = i => i % k == 0
with 2
and assigns the result of the final expression (in this case there is only one expression):
val isEven: Int => Boolean = i => i % 2 == 0
def isEven
on the other hand does no such evaluation and results in a call to isDivisibleBy(2) every time.
That means, later, when you execute the code, isEven(11)
generates in case of a val
11 % 2 == 0
and in case of a def
, you’ll have
isDivisibleBy(2)(11)
and only after evaluating isDivisibleBy
you’ll get the result.
You can add some debug code to isDivisibleBy
to see the difference:
def isDivisibleBy(k: Int): Int => Boolean = {
println("evaluating isDivisibleBy")
i => i % k == 0
}
I'd like to address another point here. This defines isEven
as a method:
def isEven = isDivisibleBy(2)
And this defines isEven
as a method as well:
val isEven = isDivisibleBy(2)
In both cases, isEven
is a method which, when called, return a function.
In the first case, isDivisible(2)
is called every time isEven
is called. For example, this calls isDivisible(2)
three times:
def isEven = isDivisibleBy(2)
List(1,2,3).filter(isEven)
In the second case, isDivisible(2)
is called once (at construction time, or when that line in a definition is executed), and that value is retrieved every time isEven
is called. The following example calls isDivisible(2)
one time only:
val isEven = isDivisibleBy(2)
List(1,2,3).filter(isEven)
I think that the main pro of defining the function isEven
as val
is to show to audience that the function can be defined this way. Then it's clear, that a function is just an object like everything else in scala. But in the world of non-demonstrating programming, there's no need to write functions as val
s.
The method def isDivisibleBy(k: Int): Int => Boolean
returns a function which takes an Int (i
) as parameter and returns a Boolean (i % k == 0
).
val isEven = isDivisibleBy(2)
on the other hand is a field into which the function returned by isDivisibleBy(2)
is stored. If you use def
instead of val
then the isDivisibleBy method would be called every time the isEven method is called, but now it's called only once and the result is stored in the field.
You could achieve the same result by writing def isEven(i: Int): Boolean = i % 2 == 0
I think the point of the example is that you can have functions which return other functions, and you can store the functions as objects, and then call them as if they were traditionally defined methods. The above code is also quite similar to currying, so that might also be one thing demonstrated by the example (although it doesn't use Scala's syntax for currying).