I did a functional programming course once.
As much as I tried at the time, I couldn't wrap my head around it. I'd struggle to write anything coherent in Haskell today, I still couldn't tell you what a monad is, or explain the ins and outs of the other mathematical concepts related to functional programming.
With time, though, I have come to really appreciate the approach that functional programming paradigms encourage. The concepts, in many cases, lead to code that is much easier to reason about, test, compose, and adapt over time.
I have a project that I've worked on for years. One of those, "I'll finish it someday" projects. I often use it to try different approaches and libraries, as it has enough complexity to make it an interesting example and I'm not too concerned about when I finish it. Sometimes, as the saying goes, it's about the journey more than the destination!
In this post, I'll go through some lessons that I learned while adding Ramda to that project, to help with taking a more functional approach.
Ramda is a collection of functions that "makes it easy to create functional pipelines". It's pretty easy to get started with, but after a while, I did find myself learning a few lessons around how to get the most out of it.
If you're looking for more of an explainer on function programming, there's plenty of other great articles for that. Here's one example:
This list of libraries and other resources is also worth a look:
Ramda has a lot of functions, so if you need to do something, there's probably a function for it.
However, I found it difficult to find what I was looking for because some of the namings seemed a bit non-obvious. For example, I found evolve useful in multiple cases for applying different transforms to each property in an object, but I only found it by chance.
Make sure to explore the docs, you'll find some interesting tools!
You've had a look through the docs and found a few interesting methods. All of these methods are great for more complicated logic, but they can also make a relatively simple piece of logic much more difficult to read.
Let's take an example - say you want to take the first element from an array, and return the element if it is greater than 0, else return undefined. With Ramda, that could look something like:
R.ifElse( R.pipe(R.nth(0), R.gt(R.__, 0)), R.identity, R.always(undefined) );
On the other hand, an equivalent lambda function could look something like:
([elem]) => elem > 0 ? elem : undefined;
While all the methods that Ramda offers can be useful, it's pretty easy to end up staring at some code you've just written, trying to remember what on earth you were even trying to do.
Ramda is there to help write code in a more functional way, not to be the only way to write code that is functional.
Once you start writing more complex pipelines, you'll eventually reach a point where you have a stage that needs access to a parameter that's passed in at the start of the pipeline.
You have a couple of options. The first is to change every other stage before the one that needs the parameter to accept and pass through that parameter. This is messy and quickly becomes complicated.
The alternative, as I found, is just to wrap the pipeline in a lambda:
(arg1, arg2) => R.pipe( stage1, stage2, stage3(arg2), stage4, )(arg1);
This way, none of the previous stages are affected and the code is easier to read.
These are as much for my reference as anything else, but I hope these lessons are useful to anyone else getting started with Ramda. If there's anything you found useful when getting started with Ramda, or if you have questions/thoughts about the above, please share in the comments!