← Previous in this series: Defining Functions

In the previous chapter, we’ve gone into the differences between expressions and statements and different definitions of functions. Now, we’re going to talk about properties of functions.

Unlike the two previous ones, this chapter will not introduce a lot of new concepts, instead it will use the knowledge from the previous ones to help us define what some function properties mean.

In functional programming, abstractions tend to be very generic and talk about properties of things, but the abstractions themselves tend to be very specific, in the sense that they usually follow well defined laws and the properties it abstract over are very well defined.

The function abstraction is not conceptually different in FP than it is in other paradigms, but properties of functions are at the very core of FP, and awareness of those properties heavily shape FP code.

Functions

The following are two properties of functions. It’s very important to keep in mind that they’re not judgements of value about those functions, they’re just straight cold properties that tell us how those functions behave.

Purity

Purity is a property of functions, and as properties come, this one is quite binary, because a function is either pure or impure. A functions is said to be pure when it doesn’t cause observable changes outside of itself, and doesn’t depend on the world outside of itself. In practice, this means that its output and computations happening inside of it depend exclusively on its input values. This correlates closely to our description of expressions in the previous chapter, although it doesn’t necessarily mean that there are no statements inside of the function, as long as those statements act only in at a local scope, as for example a local auxiliary assignment statement to set a local variable, or a conditional statement (might be worth noting here that both in Scala and Haskell if/else is an expression, not a statement like in most other languages).

By opposition, an impure function is one that causes an observable change in the world or depends on the state of the world. This mean that functions that do printing, writing to a file, writing to a database, publishing to Kafka, read from a console, query a database, read from a file, get the current date, etc… are all impure. Now remember how in the last chapter I said that println was a statement? I was a bit creative with the truth in there. In some languages print(ln) is a statement, in some others it’s an impure function that returns a void type (not always named void depending on the language), which is an uninhabited type, meaning there are no values of type void , and so the function returns nothing. In Python, for example, print is a statement, in version 2, and became a function in version 3:

$ python2 >>> a = print "hello" File "<stdin>" , line 1 a = print "hello" ^ SyntaxError : invalid syntax >>> print "hello" hello >>> exit () $ python3 >>> a = print ( "hello" ) hello >>> print ( a ) None

But in Scala println doesn’t implement any of the previous behaviours. So, what does it do then? In Scala, println is a function, but it doesn’t return a void type, instead it returns a value of type Unit . Unit , is a type that is inhabited by a single value, () in Scala. Returning Unit , is usually a pretty good hint that this functions is being executed purely for the side effects that it performs and we don’t care about the result. This difference between a void and Unit type is important because by being able to construct a value of type Unit , which you can’t for type void (because there are no values of type void ), means your function behaves similarly to all of your other functions and actually returns a value you can do things with.

In functional programming, developers usually try to segregate pure functions and impure functions, and have specific ways to deal with functions that are not pure.

Totality

Totality is usually less regarded that purity, but it’s still an important property of functions that people tend to care about in FP. A function is said to be total when every set of input values maps to an output value. It is said to be partial if any of the possible input combinations doesn’t map to a value (again this should ring a bell back to what we said about expression properties in the previous chapter). In practice this means that the function always returns something whatever is the parameter you pass to it. An example of a total function could be:

def compare ( x : Int , y : Int ) : Boolean = x == y

Because whatever pair of integers you pass to it, it should be able to return the result of comparing those integers.

An example of a partial function could be the following:

def div ( x : Float , y : Float ) : Float = x / y

If you pass 0 as the y parameter it will throw a java.lang.ArithmeticException: / by zero , meaning it doesn’t return a Float value, as expected. This also means that this function performed a side effect (throwing an exception). The div function is partial because it doesn’t have a return value for any input combination where y is 0 .

Knowing about this property is useful, because it means you can rely on a function being total to always return a result, or that you can use a partial function, for example, to filter values out (see for example, the Scala function collect which uses a partial function to do a combined map/filter operation).

Wrap Up

Next time, we’ll take a bit of a side step and look at function currying and function partial application, before taking the next step deeper into FP.

Next in this series: Currying and Partial Application →