A wave of functional programming posts, podcasts and tutorials have been emerging since the past years. People are always talking about functional languages like clojure, scala, haskell and others. Many have asked what it brings to the table: lazy evaluation, concurrency, easy to maintain (the code is very brief and concise), reactive programming and more.
As the list goes on, the attention on functional programming increases and the thought of giving it a shot comes to your mind. You start to read some articles and books. The first chapter is great and you are really feeling the hang of it. Some pages after they start talking about lambda calculus, category theory and the stereotype that ‘functional programming is hard and unnatural’ increases in your head.
If you came from an imperative language (OOP), looking at all those declarative code snippets from FP may frighten you. It is true that functional languages really have a lot of complex and beautiful mathematics behind it. Monoids, functors, monads, applicatives, transducers and many others may fear you at first. Do you really need all of that to start?
What do I need to dive into the functional paradigm?
One of the first differences that you can spot is the way of programming, which belongs under the declarative paradigm. Declarative programming focuses on what should be done rather than how (the so famous imperative programming). It is made upon declarative sentences, focusing more on creating expressions and evaluating functions rather than the way that the machine works and how the task will be done. Imperative languages often focus on a procedural approach, explicitly manipulating how the machine works and changing the states of a program, whereas the declarative programming express the logic of the operation (and do not focus on the implementation).
This might be one of the most popular examples when we talk about functional programming. On the first doubleAll
function we tell exactly how the computer should do the operation. We append the double of every number into a new array, but we also could do another operation after that (e.g. increment some counter, print it to the console). We are explicitly telling the machine how to do it. On the second function we do not care about the implementation; instead, we focus on what should be done, passing an anonymous function to map
(which applies the function to the array, creating a new one, fully handling the implementation).
Secondly, I would say one should understand immutability. This is one of the things that makes functional programming a lot different than other paradigms. It basically means that you should stop building structures and changing their properties every time something occurs in your code. Think of your structure as your school notebook; for every annotation the teacher writes to the blackboard, you put it in an empty space, one word next to another, rather than writing them one above other, stacking every word (and ending up with a very crazy and mixed page).
Functions should be treated as data and that means you can return functions or pass them as parameters and even compose then to achieve the desired result. As they are data, they should also be pure; that means if you have a function named is-number
it should, literally, check if it is a number. Don’t try to sneak any other responsibility to it (e.g. print to the console, add some records to a database): it must do what it says. Period. If you want to check if it is a number and then commit it to a database (or any other logging material), create another function to access the database. Then, you can compose both of then to your desired output. Sure that will produce a third, unpure function (database is considered I/O, moreover this new function is not pure), however, now you have two isolated functions, hence improving the maintainability of your code (and debugging!).
Conclusion
With those thoughts in mind you can start to adapt to functional programming. There is a lot more to come, however it can be a bit traumatic to try everything at once.
It is hard to change from a OOP and imperative paradigm, but keep in mind that you should take one step at a time. Instead of going “full functional” with your stack, you can start to tackle some functional concepts in your current language and exercise the ideas in your head.
Top comments (3)
I've worked with an open-source raytracer called POV, as in "point of view".
POV is a fine example of OOP. It was entirely written in C.
C, as everyone probably knows, is not an OOP language. You can write OOP in C, but the OOP part is all DIY.
If three projects are all written in C, each with their own DIY OOP, none of the OOP frameworks will interoperate with each other.
C++ is not a FP language. You can write FP in C++, but the FP part is all DIY.
Compare to writing an FP program in an FP language, like F# or Haskell, the experience is very different. And, in my opinion, a much better experience to use a FP language to do FP programming.
Programming languages are tools. Those tools are suitable for broad domains.
Using C to do OOP, or using C++ to do FP, is much like using a hammer to pound in a screw. It can be done. But not really the right tool for the job.
There are several FP languages available. I recommend OCaml, or F# (which is pretty much OCaml retooled to be suitable for .NET or Mono), or Haskell, or Elm.
I would recommend against trying to do FP with Python, or C++, or Lisp.
Not because they are bad languages... they're not bad, they're excellent. Rather, because they are not FP languages, and doing FP in those languages will not make for a good FP learning experience.
Here's a good quote from the foreward in The Book of F# by Dave Fancher, which enumerates the attributes of FP have and are easy-and-natural to do in a FP language, that OOP languages lack:
"""
I’ll wind down with a lie that OO people who are learning FP tell one another: “Learning FP will make you a better OO programmer.” It rings true, and in the short run it may even be true, but as you internalize immutability, recursion, pattern matching, higher-order functions, code as data, separation of behavior from data, and referential transparency, you will begin to despise OO. Personally, I went from being a Microsoft C# MVP to feeling guilt every time I created a new class file. Once you understand the defects that can be avoided, it stops being a technical choice and becomes an ethical one.
"""
~ Bryan Hunter, CTO, Firefly Logic
I thought POV-Ray = Persistence Of Vision?
Brings back old memories =)
I had a CAML background (there was no
O
yet, ~1990) and experience with Erlang, but LISP dialects like Clojure, Racket, or Scheme have worked pretty well for me to:Had I started reverse, I think I'd have been overwhelmed at step one.
The algebra of types / cat theory is NOT a requirement for Functional Programming, despite all the efforts to mislead wannabe FPers into believing so, at the risk of turning them away from FP forever.
Thanks for the comment!
I also think that "coding FP" in a non-FP language is not the best. However, I think one can get used to some of the concepts in a language of his or her preference. As you said, it is just a tool.
After the phase of adaptation I would jump to a FP language like Haskell or F# too. I started with Haskell back at college and never had a bad time with it, but I've seen lots of people saying that it is too hard to digest at first.
Moreover, this quote truly exhales what I've been feeling for the past year regarding OOP. I never tried F# myself (nor books), but I will definitely look at the book after reading this.
Following your thoughts: once you find you can use a screwdriver rather than a hammer, it feels much better. And you know it is right.