In the previous posts, we took a look at how functions are the core pieces in functional programming languages. We talked about pure functions, referential transparency, side effects and recursion in the previous posts. In this post, we are going to explore some properties of functions and how we can use them in a functional programming language.
A function is a black box that given an input always gives you back the same output. As we stated before, a function does not have any side effects; if it has any, it could be a procedure but not a function. Function is a term that comes from Mathematics and there is no concept of side effect in there. A function accepts a set of input values; we call that the domain of a function. The output of a function is called codomain and the set of outputs is called the image of the function. We can decompose them like this:
f: A -> B
Where A is the domain and B is the codomain. For example, for the function
f(x) = 2x we can decompose it as it follows:
Where as we stated,
A is the domain,
B is the codomain and
[2, 4, 6] is the image of the function.
Arity is the number of arguments that a function takes. We say that the arity of
f(x: Int) is 1, or that is a unary function, or that is a function with one argument. Therefore, the arity of a function can be expressed with a number or the spring term. Unary, binary, ternary, etc… Are words that come from Latin, but mathematicians usually use Greek instead of Latin so usually they interchange those words for the same ones coming from Greek. We can say as well that the arity of a function is monadic, dyadic or triadic.
Function composition is one of the bases of functional programming. The idea is that if you have a function
f = A->B and a function
g = B->C you can create a 3rd function
h = A->C which internally uses
g to create that
C, that is:
h = g(f(x)). If we express it in mathematical terms we can say that
h = f∘g readed as “h is equal to f after g” or what is the same:
h = Cgf
An example of function composition is:
def intToString(i: Int) : String = i.toString def stringToDouble(s: String) : Double = s.toDouble val composedFunction = stringToDouble _ compose intToString
We declared two functions
stringToDouble, when we compose them we create a 3rd function which accepts an int and returns an double. So if we call it:
composedFunction("32") it returns
32.0 which is the result after converting that String to int, and from that int to a Double. Notice that when composing functions, the functions are applied from right to left, this time:
intToString and then
stringToDouble. We can do the same without modifying the order of the functions with the function
andThen, this will be like this:
val composedFunction2 = intToString _ andThen stringToDouble
This is the same and in my opinion less confusing.
This last expression could be stated without infix operators as it follows:
val composedFunction2 = (intToString(_)).andThen(stringToDouble)