Hacker News new | comments | show | ask | jobs | submit login
Mostly Adequate Guide to Functional Programming in JavaScript (github.com)
114 points by codecurve 887 days ago | hide | past | web | favorite | 64 comments

For a long time I've been looking for a guide to functional programming, applied to common real-world web examples, but still can't find one.

E.g. when most of the code I write is database queries, performing business logic, calls to memcached, handling error cases/messages, validating input, and then outputting data in various formats (JSON, HTML templates, etc.) -- how/where does FP help with that?

Is FP more a paradigm for computationally intensive programming? Back-end web programming? Front-end? I come across "FP is good" all the time, but I've never found any kind of resource to help me understand the tradeoffs between FP and non-FP, where it makes sense to use it and where it doesn't. Or any kind of FP tutorial on how to build, for example, a basic web CRUD app.

Functional programming is not for back-end web programming or front-end web programming. That's like asking if calculus is for making better nails or for making better screws. Or if a 68K processor is for doing taxes or for doing word processing.

It has nothing to do at all with what kind of thing you are building, it is a general means of expressing common patterns in programs. Much of the reason people like FP is because it provides a high degree of code reuse in standardized forms, and it does so with a good bit of terseness and flexibility. Just about everything else in the FP world is about optimizing safety, performance, and reliability without giving up these benefits.

When someone says "FP is good," it's analogous to saying "well-commented code is good." As in, it's good everywhere you have the time to do it correctly. (There just happens to be a lot of debate about how to do it correctly.)

For those of us non-yet enlightened, I keep reading these kind of comments and walk away more confused.

Sure, I get what it is but what I don't get is how I would use it in my world of mutating data and side-effects.

So far I believe side-effects as we think of them are unavoidable[1]. But they can be reduce to a minimum (the maximum being writing programs as a big set of global variables, and jumping GOTOs between mutations). Using functional style avoid a lot of nasty side effects like unnecessary temporaries, uninitialized values and their modification afterward (local assignments or across procedures). This is a thing one can do in C, whereas in FP languages, you're constrained to write this way, giving a culture of combinators and patterns (tree rec, map, fold, streams, monads, ..) hugely solid and tiny, thus reusable.

I understand what you mean, because even though I love FP a lot, I still struggle to conceive whole systems (other than simple map-filter dataflow ones) this way. So far I keep the FP principles for small pieces, hoping later on I'll find a way to close the gaps.

[1] John Backus tried to design purely functional languages a long time ago and he hit the IO wall (he mentions it in some article). Haskellers had issues at first and found that Monads were a nice functional way to express IO-like logic. It's still something weird, a lot less clean than purely functional expressions to be evaluated.

The 'IO wall' as you put it is the main thing I have not been able to wrap my head around. Most of the examples I have seen of FP-implementation-of-X leave this bit out, or at best is a passing mention left as a 'reader exercise'.

Most of us probably already follow best practices in terms of small functions, no global state, promises, composability etc. But at the end of every chain I have a db query and most of those mutate some data or other.

Hopefully some day it clicks.

> Hopefully some day it clicks.

While you wait for that, try writing useful code despite not fully understanding it.

Tell me what you think of the following code examples:

    > sum . take 2 $ [1,2,3,4,5]
    > -- now lets pretend we have a list of numbers we got from the DB
    > let mockDBResponse = return [1,2,3,4,5] :: IO [Int]
    > -- first lets try the exact same code as above
    > sum . take 2 $ [1,2,3,4,5]
    > sum . take 2 $ mockDBResponse
        Couldn't match expected type ‘[c]’ with actual type ‘IO [Int]’
        Relevant bindings include it :: c (bound at <interactive>:46:1)
        In the second argument of ‘($)’, namely ‘mockDBResponse’
        In the expression: sum . take 2 $ mockDBResponse
    > -- what to do? Use <$>
    > import Control.Applicative ((<$>))
    > sum . take 2 <$> mockDBResponse
1) Applying pure functions to pure functions

    sum . take 2 $ [1,2,3,4,5]
2) Applying pure functions to monadic functions that implement fmap of the Functor typeclass

    sum . take 2 <$> [1,2,3,4,5]
When I noticed that adding brackets around $ allowed me to apply pure functions to monadic ones a lot more made sense to me.

Immutability is useful everywhere in the stack, even the database.

See "Turning the database inside out with Apache Samza" by Martin Kleppmann: https://www.youtube.com/watch?v=fU9hR3kiOK0

If your world of mutating data and side effects is anything like mine taking a functional view should let you get rid of a lot of those side effects and mutating data. The functional program will be easier to debug. Unfortunately it might also be slower but this can be mitigated by memoization or caching.

So...don't write code that mutates data and has side-effects?

It is, again, like saying "I don't see how this helps me in my world of uncommented code". You could just comment your code, right?

(Or if that's too much work, or it's a shared code base and you can't get buy-in, just start going forward?)

I belive database operations are considered a side-effect, so how would you propose I do away with those?

You're sort of coming at this backwards.

What you want to avoid is side-effects. One class of side-effect would be database operations. So let's imagine you have this code:

    let products = [{id: 1, price: 10.12, desc: 'foo'}, {id: 2, price: 5, desc: 'bar'}];

    for (i = 0; i < products.length; i++) {
      products[i] = formatPrice(products[i]);

Looks good, right? I mean, presumably formatPrice just, I dunno, turns prices from numbers into strings like "$5.00", or whatever. But what if formatPrice looked like this?

    function formatPrice(product) {
      product.desc = '';
      product.price = '$' + product.price.toString();
      return product;
There might be some reason why that's the exact functionality you want, but it's terrible practice to write; your price formatting code is performing database operations, which are side effects. Don't do that. :)

What you want is to chain together a number of simple functions. And sure, one of them could even be "saveToDatabase". That's fine. What's not fine is having some random function, somewhere, randomly mutating your database.

If you think FP requires you not to use a database, you've badly misunderstood FP. What it's asking is for you to be explicit about what you're doing. Pure functions are easy to reason about; global variables are hard. Database operations are especially hard to reason about, which is why you want them locked away safely at the start and end of your processing chain, and not just as random side effects of otherwise unrelated code.

Database operations are just state transformers, which are pure functions. It is the state you have to worry about, and you can wrap that state in a number of FP constructs.

Not trying to be snarky, but what is the difference between a state-transformer and a general side-effect.

The book in question gives the following explanation for what a side-effect is: "We'll be referring to effect as anything that occurs in our computation besides the calculation of a result."

This is in chapter 3, and they provide the following list for what is a possible side-effect:

    - changing the file system
    - inserting a record into a database
    - making an http call
    - mutations
    - printing to the screen / logging
    - obtaining user input
    - querying the DOM
    - accessing system state
With that in mind, there is not much I can do in my code and have a functional app without side-effects.

> With that in mind, there is not much I can do in my code and have a functional app without side-effects.

Not quite. Let's take one of those side effects as an example... say accessing system state.

You probably aren't just accessing system state. You probably want to update it in some way right? Your function to update it could easily be pure.

Take obtaining user input for example. Getting the user input is usually the most boring part of the program isn't it? The majority of what you do with that user input could very likely be pure functions.

You don't do away with them, you differentiate between side effectful functions and pure functions. One way to do this is tag them with the type IO. This would be a great pain if there weren't helpful ways (see Monad, Functor, and Applicative) to apply pure functions to them.

You also get the benefit that those side effects have a set of laws to govern their behavior in failure cases.

I have only tried FP a little bit, but I personally found value in non mutating data and removing side effects, even in the portions of my code that are OO.

It's refreshing getting rid of the whole class of bugs caused by developers using harmless sounding method names that subtly alter their data.

It did require substantial rewriting of the basic units of the program I tried it in, so there is that...

One of the reasons FP has gotten so popular over the last 15 years is because we've figured out how to handle mutating data and side effects in a way that is compatible with standard FP plumbing. (Though there are probably a few roadblocks here and there that need work.)

This just seems like fogging the issue.

Calculus is not for economics but there are guides to calculus for economics majors. A given language isn't word processing but a "guide to writing a word process in Pascal" is not unimaginable.

The parent asked for a guide to using functional programming for the web back end. I'm not sure why sure a thing couldn't exist.

I assume you use SQL or similar? It's not exactly Haskell-level functional, but still, you're probably already using functional paradigms.

In the case of JS, using functional techniques simplifies the code quite substantially. Testing becomes easier. Immutable data can be much easier to reason about. It's often orthogonal to OOP, functional techniques in the small, OOP to structure in the large. FRP is very useful for interactions front-end (form validation for example), but also is just all-round terrific for message passing. Recursion is ideal for certain scenarios. If you take something like handling errors, an example would be the way promises deal with them, with the ability to collect at the end of the chain, or deal on a case-by-case basis.

I'm just listing stuff off the top of my head; with a general-purpose language [with the option of different paradigms] it's not so much a case of 'build a web app', it's more 'if you use these techniques, you may well find that your code is easier to reason about'.

I think the metaphor I like is that with FP it's as if you have an assembly line of functions, and you pass your data down the line, with each function transforming in turn; each function can easily be ripped out and/or replaced and tested. There are multiple downsides and pitfalls - overly abstracted code being a very common one, but as a general approach it's often extremely useful.

Books maybe worth a flick through (tried to pick stuff that is less...abstract, & I have specific interests, so am biased) - Functional JS by Michael Fogus, Programming Elixir by Dave Thomas, Learn You Some Erlang for Great Good by Fred Hébert & Programming Erlang by Joe Armstrong

Railway Oriented Programming (F#) http://fsharpforfunandprofit.com/rop/

Netflix talking about FRP http://youtu.be/XRYN2xt11Ek

(sorry for the slightly rambling post, typing & copy/pasting ain't great on a phone)

You may want to check out Clojure and ClojureScript. Both are functional languages that let you build the backend and front-end for web apps in a functional style.

I personally like Eric Normand's "Web Development in Clojure" videos[0].

[0] http://www.purelyfunctional.tv/web-dev-in-clojure

That's a very good point. We need more examples of something practical done in a functional programming manner.

Like another commenter mentioned, FP is not a tool to use here but not there, it's a general method of abstraction that can reduce a lot of clutter and boilerplate, and make the code more terse, so that when you read it, you see what your are doing, rather than be entangled in all the details of how you are doing it.

I personally utilize functional programming concepts all the time, to the point that I get very frustrated when I have to work in a language that doesn't support functional programming.

We used chunks of FP in a large frontend application. Where it came in most useful was in the data layer of the app -- the bits from calling out to external web services, adapting that data for the frontend views, and (because we used React) also rendering those views. Functions (including quite a lot of mapping and some reducing here and there) all the way down.

We then ended up mixing in some WebGL for high performance parts, and that turned out to work best as more object-oriented code. Probably partly due to the library we used.

> For a long time I've been looking for a guide to functional programming, applied to common real-world web examples, but still can't find one.

http://realworldhaskell.org/ (sometimes code is too old to compile)

also good: https://nikita-volkov.github.io/a-taste-of-state-parsers-are...

> database queries


> performing business logic

Give a more specific example. Preferably with code, and I'll respond with a Haskell/FP equivalent.

> calls to memcached,

    import qualified Database.Memcache.Client as M

    mc <- M.newClient [M.ServerSpec "localhost" 11211 M.NoAuth] M.defaultOptions
    M.set mc "key" "value" 0 0
    v <- M.get mc "key"
More on memcache here: http://hackage.haskell.org/package/memcache

> handling error cases/messages,


> validating input

Give an example on input. Depending on complexity I'd most likely use attoparsec or maybe just stuff from the text library.

> outputting data in various formats (JSON, HTML templates, etc.)

JSON: http://hackage.haskell.org/package/aeson

HTML: Not sure about outputting html from a data type's structure, but this might be what you are looking for: http://hackage.haskell.org/package/blaze-html

> how/where does FP help with that?

First I need to know what your definition of FP is. I think it's safe for me to say without knowing your definition that it doesn't help with those specifically as much as it does with tying them all together and having a greater handle on what their behavior/interaction with eachother will be.

> Is FP more a paradigm for computationally intensive programming? Back-end web programming? Front-end?

General purpose.

> I come across "FP is good" all the time, but I've never found any kind of resource to help me understand the tradeoffs between FP and non-FP, where it makes sense to use it and where it doesn't.

That's a shame and I couldn't find really good ones either. To be fair though, such a comparison wouldn't be the easiest to create. I'll add it to my "attempt to blog about" list and we'll see what happens.

In the meantime this might help:


> Or any kind of FP tutorial on how to build, for example, a basic web CRUD app.






I'm also in the middle of creating a Haskell version of the Django polls tutorial:


Thanks for all those links, that looks super-useful. I'll make my way through them and maybe all this will finally click. Much appreciated!

(And to all the other replies/links as well, I can't edit my original comment anymore and don't want to take up space with extra comments. But all very appreciated!)

No problem. You can also find my email in my profile. Feel free to email me with specific questions. I'm interested in helping people see how Haskell applies or finding cases where it doesn't apply to their real world use cases.

I also have a few semi-useful things on my blog currently so I'll go ahead and put that in my profile as well.

EDIT: Additionally, if you experiment with Haskell be sure to use Stack[0] instead of cabal to avoid potential headaches. The Stack wiki[1] was a useful but easily overlooked resource for me as well.

0: https://github.com/commercialhaskell/stack

1: https://github.com/commercialhaskell/stack/wiki

You might already be using certain functional programming constructs without being aware of them. Contrary to popular misconception, OOP and functional programming don't have to be mutually exclusive. "Pure" functional programming (ala Haskell) and OOP most certainly are to an extent non-overlapping (since OOP has state+behaviour at its core), and pure fn-languages have no side-effects.

High-order function in java don't exist, so one would do :

  new Thread(new Runnable() { ... }).start();
A builder pattern can be distilled down as a form of partial application (or, currying, if you like. They aren't the same thing, but are very similar):

Functors (in OCaml) can be viewed as Java's abstract-generic-classes with dependency injection [0]. Note that functors in different languages mean different things.

Monads are tricky to understand [1], I have been led to believe. But if you use the promises API in javascript, then you're using a form of monad [2].

If you're familiar with javascript, have a look at: https://www.youtube.com/watch?v=m3svKOdZijA (Hey Underscore, you're doing it wrong!) I think the best way is to read a book on fn-programming. I've heard most people recommend these (pick a language, and choose a book; you cannot go wrong with any of these):

Functional DS: http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf

Real World Haskell: http://book.realworldhaskell.org/read/

Real World OCaml: https://realworldocaml.org/v1/en/html/index.html The Joy of Clojure: http://www.manning.com/fogus2/

Learn You a Haskell for Greater Good: http://learnyouahaskell.com/chapters

Learn You some Erlang for Greater Good: http://learnyousomeerlang.com/content

Systems Programming in OCaml: https://ocaml.github.io/ocamlunix/

[0] https://monadmadness.wordpress.com/2015/01/02/monoids-functo...

[1] https://realworldocaml.org/v1/en/html/functors.html

[2] https://news.ycombinator.com/item?id=4189082

The author should probably have mentioned in the Currying section that it's roughly analogous to what OOP people call dependency injection.

Rather than a "class" with one "do it" method, which also has a constructor to initialize configurations (or worse, a bunch of "setters"), just make the configuration (or other "dependency") values the leading parameters of a function. Then, partially evaluating the function to supply those values does the same thing that "dependency injection" does, only without the logorrhea of a class.

Thank you for that analogy, I think you might have saved me a bit of time wrapping my head around the FP "paradigm".

Also see Functional JavaScript [1] by Michael Fogus.


Something I've been wondering for a long time that no one seems to be able to explain is "what the fuck is a monad?" and why is this seemingly the one question about programming that is inadequately answered

It's a pattern that recurs in lots of different programming environments. It's an abstract pattern, so it confuses people who've never encountered them, and then they go out and read a bunch of terrible monad tutorials which obscure what's really going on.

Programmers are good at abstract patterns though! Look at iterators: they're obscure and complex if you've never encountered them, but now they seem really easy. The same is true with monads.

I'm willing to bet you've read 10 plain-english perfectly-adequate explanations of monads that explain them fully, and you've rejected them because they don't seem hard enough.

A monad is an abstraction pattern that, as applied in typed functional programming, has three parts.

1. A type constructor that takes a type and returns an type. Enough languages have an Optional construct now that this should be sensible to anyone paying attention.. If Integer and Optional<Integer> are types that have values, Optional is a type constructor.

2. A function for injecting a value in an appropriate manner. To stick with java-ish syntax, it'd look like Optional<T> inject(T x) { ... }.

3. A function for binding a function to a value with appropriate types. Optional<R> andThen(Optional<T> x, Function<T,Optional<R>> f) { ... }. This function has a couple restrictions on what it's allowed to do which are called the "monad laws". It's not really worth burying yourself in them. The important part is that they have the effect of requiring inject and andThen to do the absolute minimal thing that works sanely.

This really isn't complicated. It's a super-basic pattern. It keeps getting reinvented over and over in languages with first-class functions.

So why do you not hear about it outside of typed functional languages? Because very few languages give you the flexibility to work with this abstraction. In Haskell or F#, you can write code that works with any monad. In Java or Rust, you can only write code that works with a specific type. The missing abstraction capability makes the abstraction academic only. And since it's so simple, there's nothing in particular to be gained by talking about the abstraction when you can't use it.

People have made a huge deal out of monads, but they're really not complicated. Thinking they're hard or hold the secrets of the universe is like thinking the Iterator interface is hard or holds the secrets of the universe. Sure, maybe a language has some syntactic sugar to make it easier to work with them (do-notation in Haskell, for loops in Java). But it doesn't mean there's anything complicated going on.

Note the things absent from this response: IO, side-effects, purity, sequencing, basically any use case. None of them are related to the definition of monads. None of them require monads. Monad is an abstraction. It doesn't let you do anything new. It just lets you share vocabulary (and code in more expressive languages) between many disparate areas.

> In Haskell or F#, you can write code that works with any monad

F# doesn't have higher kinded types, so I doubt this is true.

A useful way to think about it for me coming from imperative/OOP programming was that a Monad is like a well behaved (governed by laws) interface.

I found solace in telling myself "The word Monad is meaningless except for the fact it tells me this abstract thing behaves according to these laws and I can use a set of standard API's and functions on it".

Languages like Haskell provide a large API for using regular functions with Monads, combining Monads together, sequencing them in different ways, and much more.

I think this comes closest of anything I've read to answering your question directly.


Just a function that returns a value of the same type. The value comes from mean nasty state modifying and/or over the network. It is the function programming portal to real world, encased in a pretty function you can reason with.

I often wondered that too. Here's something you may find useful. I know I did. :) https://www.youtube.com/watch?v=ZhuHCtR3xq8

Nice illustrations, and a worthwhile project. I'd like to know what has motivated the sequence of topics, as things I consider fundamental (e.g. structural recursion) don't seem to get a look in. This is judging just by the TOC.

(For all the innovation in the training delivery of programming training there seems to be little attention paid to the pedagogy. This is not really a slur on this book (I haven't looked in detail; hence the brackets) but too many of the online training providers just use the same old ineffective curriculum they were exposed to in undergrad.)

Seriously impressed with the communicability of your examples. A+, will share this around.

Does it treat lazy evaluation? Also, how efficient will the resulting code be, if these conventions are used?

> We'll use the world's most popular functional programming language: JavaScript

Then you won't be doing functional programming. You will be doing "somewhat functional programming" at best.

You're right, they should have added a caveat:

"Anonymous strangers on the internet will tell you that what you're doing isn't "real" functional programming, but hey - at least you'll actually be programming rather than whining on message boards"

"Of course those random strangers will not know several languages of different paradigms, and will not be programming themselves, in opposite of what you are doing."

You know that this doesn't make JavaScript be a functional language, right? It still only has some parts from functional paradigm, and those are not well-fitted, so fully functional programming in JS is awkward.

Is there a reason why you can't do functional programming in JavaScript. Unless I'm mistaken, there isn't anything about JavaScript that explicitly prevents you from doing functional programming.

Even then, I think we all understand that the OP isn't trying to be strictly pure and is simply trying to explain the concepts in a manner that those used to imperative development can easily digest.

1. No pattern matching. Libraries can't help here, it's a syntactical and semantical issue.

2. No product types. You can emulate those, but it's awkward.

3. No recursion over lists. You can emulate this, but it's awkward.

4. It's difficult to write code that is efficient, yet is side-effects free. Good luck with prepending an element to a list.

About emulation, you can emulate closures on Turing machine, though it doesn't make Turing machine a functional language. Lack of the aspect is simply lack of the aspect.

And no, JS doesn't explicitly prevent functional programming (or rather, some aspects of it). It does so implicitly.

> OP isn't trying to be strictly pure and is simply trying to explain the concepts in a manner that those used to imperative development can easily digest.

It's like trying to show how to do OOP in C. It's a different paradigm than the primary of the host language, so any educational aspect will be severly diluted, to the point of being useless to anybody who doesn't already know functional programming.

> It's like trying to show how to do OOP in C.

Actually, I'd think that would be a super valuable to a C developer. Learning to implement OOP patterns in C would provide practical patterns that can help architect C software. For non-C developers, it'd also provide fundamentals of how OOP actually works. C developers have been implementing OOP patterns since well before C++ existed.

There's lots of resources, and practical examples of this. GObject provides an object system used by both Gnome and GTK. Check out "Object-Oriented Programming With ANSI-C" for a book on the subject.

It's arguably faster and of more benefit to learn OO in an OO language, and then work backwards to figure out ways to implement those strategies in C (eg. polymorphism using structs with arrays of function pointers, or win32 send_message constructs).

It's similar with functional programming. Figuring out folds, pattern matching and monads is probably easiest in haskell or Ocaml/F#. The decision to apply those techniques in other languages without native support then becomes a judgement about the benefit.

That GObject stuff is really complicated. You can get pretty far doing simple OOP in C without inheritance and just containment.

> It's like trying to show how to do OOP in C. It's a different paradigm than the primary of the host language, so any educational aspect will be severly diluted

Says who? I've taught OOP, as a concept, to people using C. It's incredibly obvious when you see it written--once you've implemented inheritance through structural composition, it's strikingly easy to reason about how traditionally OO languages handle their object models.

(This is basically what GTK does!)

+1. I'd argue teaching OOP in C is better than teaching it in <insert OOP language here>, as then you can build OOP from first principles.

Otherwise, OOP is just this black box that has "you know, classes and objects and stuff".

Similarly: you can build a simple LISP in Javascript. You can write an event loop in C. You can implement Hindley-Milner type inference in Python. These are all great exercises in understanding exactly what these components of different programming languages do :)

1. Lisp doesn't have built in pattern matching, either. But, just like Lisp, there are libraries out there for it[0].

2. Would probably make more sense if it were a statically typed language. Don't most dynamically typed FPL need to emulate this?

3. Got an example of it being awkward that isn't helped with ES2015?

4. (item, list) => [item].concat(list)

[0]: https://www.npmjs.com/package/pattern-match

> It's like trying to show how to do OOP in C.

Pretty much how I learned OOP:

It can be nice to have language support, but it can be nice to learn a paradigm primarily as a way of organizing your program.

> No recursion over lists. You can emulate this, but it's awkward.

What do you mean by no recursion over lists? I mean, sure, lists aren't a core data type in JS (JS arrays aren't lists), so list operations aren't either, is that it?

It's not "is that it". Single-linked lists of specific characteristics (access times, construction times and stuff like that) are one of the few core elements of functional programming.

"2. No product types. You can emulate those, but it's awkward."

Do you mean sum types?

Until ES6 is widely adopted, there is no correct tail call handling guarantee, which bars using purely functional programming.

Which aspect of the functional programming paradigm cannot be done in Javascript?

Polymorphic return types, most notoriously. But also anything that's complex enough so that dynamically typed implementations become unfeasible or effectively unmaintainable.

Tail recursion optimization, lazy evaluation

Tail recursion optimization is in ES6. Lazy Evaluation is provided in multiple libraries, including lazy.js.

There are also several libraries with pure functional implementations (ex: bilby)

If you're listing a core language feature as something that "can't be done in that language", you may not understand the language as well as you think.

Undoubtedly, JavaScript triggered the functional hype, not Haskell or Clojure.

I would say it's a joint mindshare from all the various functional families (Lisp, ML, Erlang). After all, what kind of programmers do you think wrote the functional libraries that led to the "functional hype" in JS in the first place?

JavaScript's functional hype was initiated by Douglas Crockford.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact