What is functional programming?
While OCaml is not a "pure" functional language like Haskell, OCaml supports functional programming and we'll be exploring functional programming for the majority of A Bit of OCaml.
So what is it? Functional Programming is a programming paradigm based on abstract mathematics (lambda calculus, which is why you'll see λ around when people talk about functional programming). While functional programming has lots of interesting features (like currying), all of it is based about one central idea: Functional Programming represents all computation as the evaluation of mathematical functions. The consequences are enormous. |
|
Since we can't represent any computation as anything but mathematical functions (at least in pure functional programming), in order to understand the nuances to functional programming, we should begin by discussing functions.
Mathematical functions are simply mappings from inputs to outputs. These functions can be ripped straight out of an algebra textbook.
Mathematical functions are simply mappings from inputs to outputs. These functions can be ripped straight out of an algebra textbook.
f(x) = 2 * x; |
In the functions I've written in an arbitrary syntax to the left, I first define a function f, which takes one argument, x, and maps all inputs to the number that is twice the input. So it would map the number 1 to 2, the number 2 to 4, etc.
The next function, g, takes in two inputs and maps the inputs to an output defined by the previous function, f. And I demonstrate that g maps the input (20, 1) to the number 42. It would map the input (20, 20) to 80, etc. |
These mathematical functions are a bit different from the standard functions one might write in C, Javascript, Java, (etc.). It is the result of these differences that functional programming is often considered to be vastly different from imperative programming or related paradigms. (I've linked some explanations about various programming paradigms in the footer).
These mathematical functions can't change their inputs, results in the idea that all values and variables in functional programming are immutable. Furthermore, functions are stateless. What does immutable and stateless mean and what consequence do they have on your programs?
These mathematical functions can't change their inputs, results in the idea that all values and variables in functional programming are immutable. Furthermore, functions are stateless. What does immutable and stateless mean and what consequence do they have on your programs?
Immutable means that no values can change. Why is this a result of computations being described by functions?
Functions can't change values. For instance, if f(x) could change the input x, that would imply that f(12) would change something about the number 12, which doesn't make much mathematical sense, because no mathematical function ever alters the concept of the number 12. For instance, 12 + 13 is an operation that doesn't change either 12 or 13. The plus operator simply maps the inputs (12, 13) to 25 by definition. So what does this mean for your programs? It means we can't change the values of any variables - ever. |
|
This seems a little silly. Why would people ever want a program where data can never change? In C, we might rely on having global variables that we alter or having values to increment for counters, so getting rid of the possibility of doing these simple tasks seems like a problem. How do people even do things?
Well, we don't actually need to values to change to be able to do things. To find the factorial of the number 4, we don't change anything inherently about the number 4. We just need to know that the result of evaluating the factorial function is 24, another value.
In OCaml, we treat everything as values, just as we might do in mathematics. For instance, this makes sense when we are doing statistical evaluation in a functional programming language like R. We have a set of data. To get the variance, standard deviation, or any other piece of information, we shouldn't change the dataset we work with. In fact, doing so would be bad as we would change the results.
Stateless goes hand in hand with the immutable quality of functional programming. Stateless simply means that every function can ignore the environment around it and should produce the same result regardless of where it is called. This also makes sense in mathematics: The variance of a dataset shouldn't change depending on whether I find the variance before or after I find the mean. For instance, cloud computing is recommended to be stateless because relying on information being stored between times users log in is unreliable and unscalable. This, combined with immutability, means that functional programming is amazingly reliable.
For more detail on the difference between immutable and stateless, look here. These two qualities of functions, along with first class functions, means that programming with functions can feel very modular (In OCaml, everything is a module), meaning that everything can be rearranged, reorganized, composed in widely different manners and still work. Perhaps the best demonstration of modular design is Lego, where everything is built from the same little blocks, which then can be rearranged in a multitude of different ways. We want functional programming to feel modular to us, which is why I've chosen Legos to be the banner picture of A Bit Of OCaml.
OCaml vs C
This no side effect quality of functional programming is a huge shift from the iterative programming that most students coming from C are unused to. So let's take a look at some examples highlighting the differences. For instance, let’s take a blackbox function in C.
We’ll define an integer variable with global scope, call it x, and set it equal to 0. Next, we’ll run our blackbox function. Finally, we print out the value of x is.
Well, we don't actually need to values to change to be able to do things. To find the factorial of the number 4, we don't change anything inherently about the number 4. We just need to know that the result of evaluating the factorial function is 24, another value.
In OCaml, we treat everything as values, just as we might do in mathematics. For instance, this makes sense when we are doing statistical evaluation in a functional programming language like R. We have a set of data. To get the variance, standard deviation, or any other piece of information, we shouldn't change the dataset we work with. In fact, doing so would be bad as we would change the results.
Stateless goes hand in hand with the immutable quality of functional programming. Stateless simply means that every function can ignore the environment around it and should produce the same result regardless of where it is called. This also makes sense in mathematics: The variance of a dataset shouldn't change depending on whether I find the variance before or after I find the mean. For instance, cloud computing is recommended to be stateless because relying on information being stored between times users log in is unreliable and unscalable. This, combined with immutability, means that functional programming is amazingly reliable.
For more detail on the difference between immutable and stateless, look here. These two qualities of functions, along with first class functions, means that programming with functions can feel very modular (In OCaml, everything is a module), meaning that everything can be rearranged, reorganized, composed in widely different manners and still work. Perhaps the best demonstration of modular design is Lego, where everything is built from the same little blocks, which then can be rearranged in a multitude of different ways. We want functional programming to feel modular to us, which is why I've chosen Legos to be the banner picture of A Bit Of OCaml.
OCaml vs C
This no side effect quality of functional programming is a huge shift from the iterative programming that most students coming from C are unused to. So let's take a look at some examples highlighting the differences. For instance, let’s take a blackbox function in C.
We’ll define an integer variable with global scope, call it x, and set it equal to 0. Next, we’ll run our blackbox function. Finally, we print out the value of x is.
#include <stdio.h>
int x = 0;
void blackbox()
{
// Some Code, Some Details, Lots of Fun
}
int main()
{
blackbox();
printf(“%d\n”, x);
}
And now I ask, what could x possibly be? What if I run our blackbox function twice instead of once?
We can’t say--our blackbox function could have changed the value of x. For instance,
We can’t say--our blackbox function could have changed the value of x. For instance,
void blackbox()
{
x = 10;
}
This means that there is a side effect to our function. It has changed the environment/state of our program. Trying to use the variable x in a function could produce different results depending on whether our blackbox function was run or not.
Now let’s try this in OCaml, assume that the syntax is correct since it hasn’t been covered yet.
Now let’s try this in OCaml, assume that the syntax is correct since it hasn’t been covered yet.
let x = 0;;
blackbox;;
Printf.printf "%d\n" x;;
No matter how many times we run our blackbox function, x will always be 0. x cannot be changed by our blackbox function. Functions have no side effects. Even if our blackbox function tried to set x to 10, x would not change outside of the function. Consider the following:
let x = 0;;
let blackbox =
x = 10
;;
Printf.printf "%d\n" x;;
We still print 0 to the console. (I technically lie a little. Printing a value to the console changes the state of the console, but bear with me).
Consider the following in C. factorial is a function, that when passed an integer, manages to calculate the factorial of it. We have no idea how it does so.
Consider the following in C. factorial is a function, that when passed an integer, manages to calculate the factorial of it. We have no idea how it does so.
int x = 4;
printf(“Factorial of 4 is: %d\n”, factorial(x)); // the result is 24
printf(“Factorial of 4 is: %d\n”, factorial(x)); // the result is ??
What could the second printf function print? Well it essentially could print anything. There is no guarantee that the first factorial function does not change x. With functional programming, because there are no side effects, we can guarantee that factorial x will always return 24.
Consider this binding of x = 4 as a simple renaming. Just as if we were to create a variable called pi and bound it to 3.14159. We wouldn't want this value of pi to be changed. x is just a renaming of the value 4.
So in conclusion, functional programming retains the immutable and stateless qualities of lambda calculus (as well as first class functions), which allows programmers to build modular code (which can lead to more elegant code) that is safe and robust. This means functional languages can build scalable code, which feels future proof as the world heads toward cloud computing.
Consider this binding of x = 4 as a simple renaming. Just as if we were to create a variable called pi and bound it to 3.14159. We wouldn't want this value of pi to be changed. x is just a renaming of the value 4.
So in conclusion, functional programming retains the immutable and stateless qualities of lambda calculus (as well as first class functions), which allows programmers to build modular code (which can lead to more elegant code) that is safe and robust. This means functional languages can build scalable code, which feels future proof as the world heads toward cloud computing.