Ramda: A practical functional library for JavaScript programmers
ramdajs.com
ramdajs.com
You can do immutable transformations without getting bogged down in FP abstractions.
I've been using Ramda for years and I'm really struggling to see how any of the code in our projects have been complicated by it vs lodash or any other similar utility library.
Idk what type of code bases you guys have worked on but I've never had anything close to a situation where it complicated my Codebase or had other devs who were confused by it.
I can imagine someone abusing currying and getting fancy with 10 levels of functions wrapped in another function that gets passed around but honestly that is just bad programming.
Simplicity and clarity is something that is necessary in all programming, FP is not unique, and this library doesn't push you one way or the other, unless you use it for absolutely everything.
I think this would be my only argument against Ramda vs something like Lodash, at least in a project with other contributors. Like, I’ll never use the currying because it doesn’t appeal to me. But if another engineer does, and uses it extensively, then we have a situation of “because it was easily available, someone did something that makes the rest of us have to stop and pick apart during review/debugging/whatever”.
IMO it's one thing if you're writing an engine or library where you have to ensure extremely high quality and good coverage. Or maybe you just work for an incredibly well run outfit where newcomers are deeply trained in the ins and outs of your codebase and style. But no place I've personally worked with has been like that.
If you're just building a bog standard business app for a small or medium business, I don't think purity of code is typically the primary bottleneck. Likely turnover and poor documentation and lack of on-boarding and sudden frequent pivots will be bigger deals, all of which are significantly complicated by over-abstracted code that can't be modularly changed by a single developer.
People worked with vanilla HTML and JS for decades, maybe using server side includes for some basic DRY. It wasn't pretty, it was hecka tedious, but it also wasn't Inception-level nightmarish like some modern abstractions are. There's an ideal level of abstraction for a given team and codebase, IMO, and it's not always "more abstraction is better" (not that you were arguing that). It's just a process of discovery and design by fire that gets you there, not usually a lone genius that produces a masterpiece in any one particular style.
You replace most core JS constructs with higher order functions.
From there stems your observation.
Ramda code was just impossible to read and made every 5-min job take hours or days... not worth it, no matter the ideology.
I think your issue has more to due with functional programming in general than rambda vs lodash. It’s not for everyone but personally I really enjoy it.
Arguably, depending on what you're building, the pain of integrating an entirely different language into your stack is greater than the pain of simply using something like Ramda (or, better yet, just writing simple, imperative code). YMMV of course—if the problems you're solving lend themselves naturally to purely functional code, maybe something like Rescript or PureScript may be worth it, but for most projects, I'd guess probably not.
Idiomatic describes the common practices of the ecosystem, without the features (specifically currying, since it's the only one mentioned). How does a codebase that doesn't use these features become unidiomatic?
For instance, the audience that sees fp-ts as a necessary, useful library to code in a manner they find most appropriate will find JavaScript/TypeScript ergonomics verbose, hard to read, and require someone with functional programming experience (see the verbosity according to their docs of TypeScript+fp-ts vs. PureScript: https://gcanti.github.io/fp-ts/guides/purescript.html). That audience would be better suited doing compiled option such as the aforementioned PureScript, or js_of_ocaml, ReScript, derw, Idris, GHC.js/whatever the new GHC compiler JS output is, or similar option. These languages will have the idioms of fp-ts without the heavy reliance on singularly library most of the TypeScript community doesn’t understand that bloats their code bases without requiring the safety (someone can just call .value on a Maybe/Option breaking the safety). (This is not a knock against fp-ts which is a marvel in & of itself, but the audience doesn’t line up with the idioms of the average TypeScript project).
Ramda & fp libraries in general seen to have faded a bit. The utility library everyone knew, lodash, hasn't shipped an update in 2 years. Weird to see this kind of stuff falling away a bit.
In case you wonder what I mean by "algebraic effects" - your code can make a call to something that may or may not return, is defined based on its environment (was set in some code above in the stack) and may have any side effect. Think "try-catch" but with extra operator to go back to place where exception was thrown and continue from there.
- https://github.com/sanctuary-js
I think there's new libraries now rather than the older Ramda and Lodash. The JS ecosystem just seems to move onto newer variants of things over time.
Thanks for the links. I know I've seen @gcanti's name a thousand times already, but had utterly forgotten fp-ts.
fp-ts is something that's theoretically superior and far more thought-out from an FP-perspective. But I wouldn't dare throw that on the lap of a junior programmer. They'd avoid using it anyway.
Not sure why everyone is hating on Ramda, it's extremely easy to switch from Lodash to it and using a data-driven functional approach is very natural in JS/TS. You don't need to use currying at all when using Ramda, that's optional. There's also absolutely no requirement to go full FP to use it, nor does it need to underpin every function.
Utility libraries are exactly that, they are basic helper functions when you don't want to write it yourself. Nor should you have to use ChatGPT every time you need array unions and safe hash merging or slightly more complex map functions or the 10's of other basic stdlib type functions that JS doesn't have.
I wish the docs had more examples or explanations
> Disclaimer. Teaching functional programming is out of scope of this project, so the documentation assumes you already know what FP is.
Personally, and some might disagree here, but I think you simply shouldn't use fp-ts before you have a solid experience with "real" FP languages, because any usage of this library is quite messy on its own (after all, JS simply wasn't created for this) and it's easy to create even more mess and get frustrated about it later on. It's also IMO a bit useless to try and teach it because in order to get a good grasp of the abstractions you'd have to practice them yourself and see how they help your real code, and it's something that's much harder to do in JS than in a language which has you covered. You'd rather start with Haskell or PureScript if you wish to learn more.
Inline 'debug' pipeline functions are helpful for the second problem you mentioned.
I had fun with Ramda but I experienced some perf hits when doing big pipe/map/reduces on large data structures
When the company finally came around to using TypeScript, devs could once again reason about code and data and relationships between functions without resorting to abstraction, and the FP stuff died a quick death.
Don't write code that is so far removed from the actual business data that it becomes hard to reason about cause and effect.
Abstraction is something else entirely and is usually what you want. Think more like, "things that have provable laws." Eg: concatenating two lists results in a list that is at least as long as the longest input list, for all lists. Concatenation is the operation on lists that is the abstraction... once you have defined it you no longer have to think in terms of loops and building intermediate lists.
Both were codebases were TypeScript. I think I was more effective in the second, FP, codebase than the first. But really it's more about finding the right balance. One of the flaws in either approach is you try to fit a paradigm forcefully: trying to write Java in JS/TS or trying to write Haskell in JS/TS. The TS code will be better if you understand and accept the JS/TS paradigm and apply patterns where they fit elegantly.
I don't think it makes sense to be "oop" or "fp".
Different problems have characteristics that lend themselves to different techniques.
Difficult code based come from purists who value theory over practicality.
I have a very strongly enforced "don't be clever" rule for teams I manage.
Code should read like a story, good code reduces congnitive load.
I've seen some chains of map, reduce, filter, etc... written by FP purists that are difficult to decipher.
Good code is a love letter to the next developer.
In many cases vanilla/imperative js is more readable and terse, no need to bring functional fanaticism everywhere, just in places where it gives true benefits and in form that can be understood by peers.
Functional code can be beautiful and can also be unreadable/undebugable. Same with imperative code. It's great in js/ts you can pick approach where the problem is expressed more naturally and mix those paradigms at will.
[0] https://github.com/preludejs/generator
[1] https://observablehq.com/@mirek/project-euler
I think, in any project, it’s best to repeat yourself more, and patiently watch to see which abstractions actually emerge.
Which is lodash already, wonder why your team feels like the needs to make and maintain a new component for that is valid. Furthermore, lodash is somehow optimized for it's purpose, so the custom implementation may be inferior.
I never aim for file size but AFAIK lodash support tree shaking which is good enough.
In a professional setting I will probably always reach for Lodash due to it's maturity and mindshare. Personally, though, I really prefer Remeda (https://github.com/remeda/remeda) as a pragmatic and flexible API.
stick to stdlib and native lang constructs.