Functional programming — Flow
So in the first article, we saw the main why's in functional programming, in the previous article we gave a look at functions in the context of functional programming, and in today's one I want to write more about the control flow of our functional programs.
What Ifs?!?
Yeah, in functional programming we have ifs, but we don't normally use them(whaaat?). Ifs are not widely used in functional programming because we have more clean options of control flow by managing it at the function level of our application
main v1 = do
if v1 == 5 then
print "v1 is equal 5"
else
print "No it is not."
But what are those options?
Pattern Matching
Pattern matching is a way of controlling the flow of our program by creating multiple versions of our function depending on the parameters passed:
pmatching 5 = do print "v1 is equal 5"
pmatching _ = do print "No it is not."
Now with pattern matching, I did the same thing in just two lines of code in a much more clean way. In the first ‘pmatching’ instead of declaring v1 we put the value five because if our v1 is a 5 then we want to print “v1 is equal 5” if v1 is anything else we use ‘_’ that means it doesn't matter the value of v1 because it certainly is not a five.
Guards
Very similar to pattern matching, but still containing comparative operations, are the guards:
guards v1
| v1 == 5 = do print "v1 is equal 5"
| otherwise = do print "No it is not."
Very similar to a normal function, but we add the ‘|’ symbol, a comparative expression ‘v1 == 5’ and then what we want to return. If any of our comparative expressions match otherwise will evaluate as true printing “No it is not.”.
And my loops?
Well, good news here to iterate over a collection you don't need to change state with a loop, you are free to recurse and express your logic with map, filters, and reduce functions
If I need to sum a list instead of a loop, I can use recursion like:
sumList [] = 0
sumList (x:xs) = x + sumList xs
We use pattern match to see if the list is empty if it is then we return zero the neutral value of the list, if is not empty we take the first element and attribute it as x and the rest of the list we represent as xs then we take x, and we sum it to the recursive call of the remaining list our xs.
It can also be done with a reduce:
foldl (+) 0 xs
A reduce will consist of a function that will take a function(x + x), an accumulator(the start value in our case the zero), and our list, and we’ll get the sum of the list without worrying a lot by just using a generic reduce function.
Now maps and filters
A map is a function that will take a function and a list and will return a new list of the application of the parameter function to every value in the list([f(v)])
map (\x -> x*2) [1..3] -- outputs [3,4,5]
And very similar there are the filters that receive the same parameters a function and a list, but we receive a boolean function and if the boolean function evaluates to true we keep the element in the list if not we remove the element of the list
filter (\x -> x > 1) [1..3] -- outputs [2, 3]
These are the main flow controllers in functional programming, I think when you get used to it the code becomes much more intuitive than to use a loop that by its nature modifies state and ifs that are normally very coupled in imperative languages
In general, in functional languages, we get a great improvement in readability and expressivity with our code, and it's super cool and practical to be able to do very useful flow control structures without a lot of boilerplate code, feels like caring about what is being done instead of writing characters in the text editor.
Hope it was pleasant to read for you, and thank you for coming until here, see ya