Functional programming in JavaScript is an antipattern
hackernoon.com
hackernoon.com
I'd say functional programming is a tradition that draws from sources like denotational semantics, lambda calculus, Church, Landin, etc etc.
There are some different subspecies of functional programming: the Lisp family that draws from AI engineering, MIT, Emacs, actor research via Scheme, etc; the ML family that draws from typed lambda calculus, inductive definitions, and logical proof systems; and more, like concatenative languages.
JavaScript was always inspired by functional programming, as evident by Eich's claim to have tried to sneak in a variant of Scheme dressed up as Java. I always use lots of anonymous functions, higher order functions, and nonmutating transformations in my JS, without using any special immutability libraries, and I'm pretty happy with it.
As for JS, I've read "Javascript: The good parts" one day and realized that I can do everything I want using just functions and closures - it's really elegant imo. (even use this approach in Python sometimes)
This is called higher order functions (HOF).
FP is a --in my humble opinion-- broader topic describes a certain programming style. HOF is one thing in there, but so are "function+data rather then classes" and "favor immutability".
In contrast the imperative programming style is not merely about "not having HOF" or "mutable variables all over the place".
(these properties names can be wrong as I've not study calculus in English)
So on the machine, Immutability plays an important role on the first piece, uniqueness, so it tries to guarantee that results are not just "equal objects, but different instances", which breaks the function definition.
Of course that functions that return a new object for the same arguments, even if it's immutable, are not true math functions.
I will leave that last sentence as I wrote it, but it immediately leads me to wonder if functions that write functions have wandered into the self-modifying code realm?
Immutability and Referential Transparency help reduce that friction, but in the end we still have not found a real solution. That's why languages like Haskell require a bunch of gymnastics just to allow IO to be seen as pure functions.
For example, Common Lisp has two versions of list functions that modify list - one that returns a copy and another that returns mutated original list. (By the way, Paul Graham in his On Lisp, which is from 1993, mentions that functional programming style means that function doesn't modify its arguments and rather returns a copy.) Because once upon a time, the difference actually mattered. Today, mostly it doesn't.
Other languages delegate this stuff to the implementation language/runtime, for example Java/JVM.
Of course, even then, C was functional by that definition (as long as you could remember where the parens & asterisk went when defining your function!)...so what do I know.
On the other hand, if you have the attention span and interest, Li Haoyi has an interesting (and, in its conclusion, useful) take on this topic:
http://www.lihaoyi.com/post/WhatsFunctionalProgrammingAllAbo...
That said, and being a pedant, I must say that while English (or any language) does indeed change, it doesn't do so by any kind of consensus. People don't stop and give consent to a change in meanings, they roll along with it. There are some mechanisms in this shifting of meanings (needs to describe new concepts, fashion, immigration, etc) but consensus is not one of them.
FP isn't a technical definition. It's a cultural, family-resemblance definition that's driven by some mixture of fashion and genuine exploration into a niche of ideas in computing and formal languages.
The best you could hope for would be a set of shared values of the FP culture and then justifications for why various things that call themselves FP feel that are upholding those values. Even with a definition like this though, the values will shift and waver over time.
Here's my FP values:
* Simple over easy.
Rich Hickey's old egg. Favor abstractions with few moving parts,
reduced interactions, and more parsimonious overall models over
ones driven by metaphor or target use case alone. Result is
tools which are perhaps harder to get started using but have
better complexity scaling properties.
* Mathematics has been doing this a long time.
PLs are just formal languages and mathematicians have been working
seriously on formal languages for at least 150 years: steal their
ideas. From this we get ideas around comparative linguistics,
semantics, various proof/reasoning mechanisms and types.
* Readability counts, but not just in the sense of syntax.
Really this means "legibility" or even "static legibility". It's
another hint toward types, immutability, and general state space
reduction. It's a strong push away from "emergent behavior" to the
greatest degree possible. Things should endeavor to do what they
say on the tin... and to to the greatest degree reasonable say things
on the tin in such a way that the information is available from the get-go.
* Tradeoffs between power-to-construct and power-to-analyze.
This is everywhere in formal languages/PL, but it's especially strong
in FP since there's a focus on (simple over easy) semantics. It opens
the door for there to be mathematical semantics as opposed to just
operational semantics and this makes for rich opportunities for on-the-tin
static reasoning (equational reasoning, changing interpreters, embedded DSLs).
I think these values more or less were in place back in the old Lisp-y FP days, but they really are taken to new places by "more modern" takes on FP.My advice to the author is: choose ImmutableJS, Ramda, OR lodash. Don't choose all three.
I think that is his anti-pattern.
> I could be more productive if I didn’t have to wonder things like ... “Should I mutate this variable?”
If you're doing FP the answer is likely 'No.'
Ramda doesn't even have the same functionality as Lodash.
I was just reminded of last week, when I needed a utils lib. I read many good things about Ramda, but then I wanted to debounce a function and it failed me, haha.
We can either choose to use a library like ImmutableJS instead of regular JS objects and suffer the impedance mismatch and exponential increase in verbosity, or use regular JS objects and make full copies every time we want to change any piece of data.
It's a very unfortunate blemish on an otherwise surprisingly pleasant experience (functional programming in JS), especially when paired with a nice utility library like Ramda [1].
I tried to look into if native persistent data structures was on the roadmap for a later version of ECMAScript, but this random proposal on GitHub [2] was the only thing I could find, and I'm not familiar enough with the standardization process to be able to gauge how much traction that proposal is getting.
If anyone else is aware of similar efforts, I'd love to hear about them.
Until something like this makes it into JS proper, I'll be putting my weight behind ClojureScript.
[2] https://github.com/sebmarkbage/ecmascript-immutable-data-str...
I don't mind exploring interesting avenues like immutability, jsx, typescript, declarative or functional styles. I like that JS has enough flexibility to make all of these possible.
I get the fatigue about the endless stream of new frameworks, libraries, approaches, and "new hotness". But I became much more relaxed about it all when I realised it's all completely optional. I can just stick with vanilla JS if I want to, and cherry-pick interesting things as they whizz by. It's the sushi train of programming environments :)
LISP in JS sounds great, I might give it a try. But this does raise a conundrum... if LISP is the ultimate programming language because you can write all the others in LISP, then the fact that this is a LISP written in JS makes it...what?
I'd love a tool that enabled me to spend my cognitive currency how I want to, but so far all I've found are tools that have disguised their expenses in new and interesting ways, like tranches of a collateralized debt obligation.
That's not what I meant. I may have not been clear enough.
My initial thought was that since JS is the target environment for a LISP compiler, it would necessarily have to be more flexible and "powerful" (whatever that means). With a bit more thought I realised that that's not the case.
Thanks for the prompt to look into this more, and learn :)
This isn't Python we're talking about here ;)
Yes, in some environments you don't get to choose, but why blame the language for that?
Python has Hy (newest Lisp on the block), Java has Clojure (currently the most popular one), Erlang has LFE (For BEAM just like Clojure is for JVM), Most scheme (kinda like lightweight lisp with a 100 variants) interpreters are in C. What does that make them? Nothing more than what they already are.
What makes Lisp a Lisp is Functional Programming and Syntax where the program itself is treated as Data (Very good for easy macros that transform like a Transformer) and S-Expressions (Every thing is an S-Expression). Not because you can write all others in Lisp.
> Clojure compiles to Javascript... So any Javascript job could be a Clojurescript job
That's not how it works.
The article then shows ClojureScript as an alternative.
In the comment section Ken Aguilar mentions two other alternatives: PureScript and Elm.
But there are more! Bloomberg's BuckleScript (which is OCaml; possible used on onjunction with Facebook's Reason), GHCJS (Haskell), are two other FP langs that compile to JS. These two have the added bonus of being strong langs for performant server-side programming as well.
https://github.com/BuckleScript/bucklescript
F# has a JS transpiler via fable http://fable.io/ (Elm and its architecture has even been recreated to a degree - https://github.com/AnthonyLloyd/Elm)
I have recently started playing with Bucklescript and am really loving it. There is bucklescript-tea which is a port of the Elm architecture to OCaml. So not only do you get the goodness from Elm - intuitively well architectured applications, but you can do it in a language that can be used everywhere - not just on the server.
Another worth mentioning is Typescript. Having discovered the joy of types through Elm and Bucklescript it was a real pain having to go back to maintain a fairly large javascript code base. My initial thought was to rewrite the whole thing in OCaml, but that would take months if not years. Instead I have started moving it across to Typescript. No code needs rewriting, I can just slowly one file at a time start adding typing information in. I have found a lot of subtle bugs and bugs I knew existed but was tearing my hair out about just easily presented themselves via compiler messages.
I will never go back to raw javascript again.
Sure it's worth all mentions (and so is Flow), but they do not specifically promote FP. And that's the topic of the article we're discussing :)
If you're doing FP with immutable data, there is no ambiguity: you never mutate. Doesn't matter which language you're in, nor whether the data-structure is mutable or not (e.g Redux mostly uses normal mutable JS objects, with the spread operator to avoid mutation when returning a new state).
I would be interested in hearing strategies of mitigating this, my first step has been to let Immutable creep through the project, but adopt a convention of creating a layer through which guarantees JS output - anything that is logic related in my redux layer is Immutable, anything which is in my presentational react layer is JS.
The difficulty then, is at input points where I need to always remember to deal with nested data structures too.
It feels like a type checker would be of great use here to add additional guarantees about any data
Also worth noting that if you are going to do Immutable -> POJO conversions, you should avoid using `toJS()` as much as possible. The conversion process is expensive, _and_ it always creates new object references. Either use `getIn()`, or use memoized selectors to ensure the conversion only happens when the data actually changed.
My philosophy is when providing the POJO from the immutable data structure is to select the absolute minimum from the bigger object, which I suppose is in line with your advice on using getIn
If JS had more builtin functional primitives (i.e. well beyond Array.prototype.map and friends) this problem would be much smaller.
Similarly, I think another way out of this antipattern is to settle with the team on a single way to do FP. This disqualifies ImmutableJS by definition, because 3rd party apis tend to want arrays and objects. But most other options are open, I guess.
I think I have a better answer than his conclusion that it aint popular because people shun things that aren't popular.
(1) coding when EVERYTHING is immutable by default is a royal pain in the arse.
(2) Coding directly in Abstract Syntax Trees is not pretty, there is a reason why most programming languages don't look like lisp.
Not sure I'd agree with that. Scala is a pain to write at times, but it's not because of the immutable data structures--indeed, the APIs are structured such that it's pretty painless. And while under the hood Kotlin's "immutable lists" aren't actually immutable (they're just ArrayLists, the interface is more akin to C#'s IReadOnlyList<T>), the API they present on top of it makes this pretty straightforward.
In both cases, you can do mutable things, but it's extra work.
I tend to think it's more of a Clojure-specific thing, and I think your #2 is more to the point: people don't use Clojure because it looks funny and is foreign to read for people who don't already know Lisps, that's all.
(I've used Clojure, but I don't claim to "know" Clojure. YMMV.)
Weird thing is that functions are, for the most part, also a tool to make abstractions, but for some reason, the perception of them is different.
I came to this conclusion after discussions with people I know who don't like these languages, and I now think it comes down to this.
Is it really? Because I haven't noticed writing my Erlang.
I think it is, but I'm biased :). Still, I don't think this is the reason. I believe it's simply because, due to accidents of history, C won the popularity contest, and most languages followed with (incrementally altered) C syntax to stay close to what's popular.
The reason a lot of programing languages don't look like Lisp is that most programming languages don't have anything interesting semantically going on. Their authors' primary source of intellectual pride is the work that went into the syntax.
Part of it is CS education. It is drilled into the heads of CS undergraduates that programming language design involves tokenizing, and parsing with LALR(1) or what have you. The idea that you're going to have those pieces there if you design any language for any purpose is deeply ingrained, like the idea that no matter what you will be coding, you're going to have modules with functions that have local variables, and that there will be a edit-compile-debug cycle, and so on.
It was widely panned in the comments then, too.
EDIT: looks like it was posted even earlier and nobody payed attention: https://news.ycombinator.com/item?id=14590127
Of course you canb choose to live in the past and keep on hand-rolling your Javascript just like others do with C and Java, but the world has changed. In today's world we write our code in a high-level language and have an optimizing compiler emit the assembly/machine code that our compute engine executes. The details are a bit different for different compute engines.
For C, the high-level compiler might use the same backend as the C compiler to emit machine code for x86 or ARM, but it will make integrating C libraries easy to do. For Java, the high=level compiler may emit Java class files directly but it will make integrating Java libraries easy to do. For Javascript you have Scala.JS and Clojurescript emitting optimized Javascript because the compute engine executes that directly.
As a developer it is worthwhile knowing all these low level details of internals because you will need to debug issues with that level of knowledge. But your main job should be to implement tested stable functionality for the users of your application. No matter how much you like C or Java or Javascript, it is always faster, more efficient and more reliable to write in a truly high-level language that incorporates the best knowledge of the latest research in Computer Science.
As a Javascript expert you may find that Clojure slows you down at first. That is the investment period. Then you start picking up the tempo, enter the payback period, and people start whispering about you being a rocket scientist or 10x developer.
Of course you could just stick woth Javascript but others who know how to roll with the punches will change with the times and win the 10X crown. Not because they are better, but because a true 10x developer is just a person who uses the best tools. They used to say that the suit makes the man. Nowadays it is the tools that make the 10X developer.
That hasn't happened to either C or Java.
Latest research? Clojure is a Lisp family language, JS is an Algol family language. Both Lisp and Algol are from the sixties. Algol family is just more popular, so when people encounter Lisp family for the first time, they think it's "new".
Neither C nor Java are assembly languages. What are you trying to express here?
> No matter how much you like C or Java or Javascript, it is always faster, more efficient and more reliable to write in a truly high-level language that incorporates the best knowledge of the latest research in Computer Science.
> always faster, more efficient > truly high-level > best knowledge > latest research
I uh.... what?
I have heard criticisms of lisp macros as making it a write-once language. If you're not working on a team then maybe it's fine, but other people aren't gonna want to figure out how your macros work.
Plus they are selling themselves short. If you want to fully learn something you must teach it. By documenting your code you are teaching the other developers on the team and this will lead you to much deeper understanding than the hackers who churn out code all day.
I mean, you still have to THINK about whether you need to keep something immutable or not, but that's the difference between a junior developer and a more seasoned engineer, right?
Even though interop is possible and not difficult, I am so much more comfortable using cljs wrappers (there are a lot) or in my part of the world (cljs - land). One reason is that the clojure way (tm) is to use immutable data and pure functions. It bugs me to enact stateful changes.
One way out is to have a datastructure that stores the state of the entire browser (what url am i on, what's in local storage?) and change the datastructure while some unspeakable side effecting function observes and updates either localstorage, or browser history, or etc.
In Clojure 90% of my code is datastructures, manipulating them, and sending them back. Partly because servers tend to be way less stateful than UIs, and partly because it feels like there is more to explore in Clojure without going into java, compared to cljs/js.
Are there immutability constructs in flow or typescript that would allow libraries to conform to a type interface for conversion?
This, then, would solve almost all of the problems mentioned in the article, yet remain functional friendly.
Ramda functions never mutate the object by always returning a new object, this is very simple to reason about, and it's pretty close to immutable programming without the overhead yet only one API.
"_.curry(f,0)" instead of "x => f(0,x)"
Ergo author is claiming javascript is an antipattern, tldr clojurescript. /shrug
Basically, write Javascript without functional programming techniques. That doesn’t seem like a good solution.
So . . . what am I misrepresenting?