I disagree with the notion that functional programs must never contain side effects. I think we should just be reasonable and only produce side effects when explicit, and never mix side effect code with data processing.
We shouldn't have to rely on loopholes to satisfy any hard and fast rules. A lot of writing good code is just being reasonable and explicit.
If you call a function to launch a missile, you'd expect that it's just going to launch a missile and do nothing else. So why are we so concerned that it contains side effects? What other option do we have?
Logging is a bit different. For many, logging is probably a necessity to allowing you to look back and and understand context when things go wrong. But when you're programming functionally, I find that it's much easier to get a grasp on things compared to procedural code, which can become spaghetti much quicker. I also find that bugs are much less common, or otherwise much more obvious in functional code, negating the need for logging in the first place (for functional code).
Pure functions have of course a lot of benefits - if the goal is computing a value. However, why would you even want to compose an interactive application (in javascript) fully out of pure functions?
E.g., suppose you have an SPA that pulls data from a number of Rest endpoints, then does some transforms on that data, produces some HTML and eventually updates the page DOM accordingly.
It makes sense model the "transform" and "produce HTML" steps as pure functions, because they are effectively side-effect free calculations. I think it might also make sense to cheat and treat the Rest calls as pure functions, because this will let you find out quickly in which order you should do them and which of them you can run in parallel.
But why would you want to model the eventual update step itself as a pure function?
> But why would you want to model the eventual update step itself as a pure function?
So you can more easily debug the application across incorrect state changes: if your update function is pure, you can very easily compare the state-before and the state-after.
This maintenance of observability is a superset of the unit-testing bit legulere talks about, and also opens up new features e.g. undo/redo becomes trivial, just re-set the state to whatever you need. If you modify the application state in-place, things become much more complex.
In a way git can be treated as that, the HEAD commit is the application state, when you "modify the state" you don't modify that commit in-place instead you create a new commit, then move HEAD to that new commit. Only the second step is impure. Even amending a commit is the latter, you're not actually modifying the old HEAD, you're creating a new one from it then moving HEAD from one to the other.
In theory, this sounds great, and I am a huge proponent of functional programming. In practice, I've never seen it intuitive or useful.
I've been at 3 companies now where "time travel" has never been used and web app development has been more painful than it needed to be.
It feels very much like the proper (from a CS perspective) approach, but the language and libraries just aren't there to make it feel productive or fun. Is there any meaningful work being done to address this, or is it perhaps a boiling frog scenario?
> In theory, this sounds great, and I am a huge proponent of functional programming. In practice, I've never seen it intuitive or useful. […] Is there any meaningful work being done to address this, or is it perhaps a boiling frog scenario?
That's how Elm works and it's glorious. IIRC there are also clojurescript systems working that way (application state is an atom and updating it is a transactional CAS).
But of course it also helps that the language itself lends itself to this, so you want a language which is expressions oriented and there persistent data structures are the default (or even the only) option. You can do the same thing in e.g. Javascript, but the language doesn't really help and it's easy to slip.
You might be able to foster a deeper appreciation for some of these ideas by playing with Elm a little bit. Some time ago, they implemented time-travel in the standard debugger, and its a really wonderful feature. The site also has some demo apps that make it easy to play around with that time travel support.
I agree that, currently, languages and frameworks don’t make things like time travel easy, insofar as such a feature is not often built in. However, technologies like Flux and Vuex are paving the way for such things to become mainstream. It’s worth pointing out, too, that both were inspired by Elm’s runtime architecture.
Functional programming is, only in the last half-decade or so started to see a relatively dramatic rise in popularity. I think this trend will continue for a long time to come. For that reason, it’s worth learning more about these concepts.
Like another commenter said, using frameworks like flux and vuex allow you to utilize browser extensions that make the 'time traveling' debugging process easy.
I could be mistaken, but I don't think you can have a "pure" functional front-end. I think even with something like mithril.js, you will have to run some computation method to update changes in the view when some data changed.
I think the benefit of js functional programming is composability; you can build complicated functions from smaller, more focused functions. This ends up being easier to read and debug if you run into some unforeseen logic error.
It really depends on what you are building, but it's hard to avoid logic errors when working with the DOM explicitly. It's a lot easier to map over an empty array than to manipulate DOM elements that have been removed from the screen.
I agree with you, functional programming and pure functions have a lot to offer but they have a place and as soon as you start pushing them into every corner of an app you end up just creating problems for the future.
I don’t think that’s entirely true. A lot of the ideas talked about in this article and that most people have already become familiar with are not very abstract. If you tried to build a large application out of nothing but pure function application, of course your code would become complex. We need more frameworks or libraries to take some of these ideas and bring them together in a way that scales. Elm, Vue and React are examples of tech that are doing this and, I think, make functional programming extremely appealing.
Better tooling can help, but so can 'not' forcing everything to be side-effect free.
Honestly, I'm getting downvotes for this but probably from the same types who thought CQRS or micros-services should be taken to the extreme and ended up totally crashing and burning.
Why can't techies see an approach, appreciate where it can help, and apply it where its useful? Why does it seemingly have to always be an all-in-or-nothing proposition?
> Why does it seemingly have to always be an all-in-or-nothing proposition?
This is one of the reasons I love JavaScript so much, and one of the things that stood out to me in the article. JavaScript is extremely expressive and allows you to write code without worrying about following some specific paradigm, which always seemed to me to be one of its biggest strengths, not a weakness as the article seems to suggest.
I was left wondering “Isn’t what he calls Effect really similar a Haskell Monad?” for a large part of the article. I was really glad to see my novice FP insticts were right!
Let's not forget that if you are not passing immutable data-structures into your pure functions, all bets are off.
Unless you code very defensively, there's no way you can guarantee purity, especially in async programming.
Given f(x) = y, if x is a regular JS object, you can't guarantee purity. It works many times because of the single-threaded nature of most JS engines, but the moment you do something async, you can't guarantee x hasn't changed.
That's one of the many reasons I like ClojureScript. It offers interesting guarantees that let me write robust code.
As long as JS lacks partial application and the ability to define infix operators (for monadic `bind`, etc), it's always going to be ugly. Not that JS would be a good language for that to begin with. We need a serious functional language that compiles to WASM.
Really? If OCaml has a compiler to Wasm, I’m not aware of it. I’m only aware that it compiles to JS (via js_of_ocaml or Bucklescript). Could I please have a link to the Wasm compiler if you have it?
I read somewhere that an OCaml to LLVM compiler was attempted, but people had problems with the GC aspect. I don’t know if this claim is true, though.
It compiles to wasm object files and links files with LLVM's LLD. My current challenge is ensuring that the stdlib correctly translates to wasm. The C parts are not translated yet - which means: no gc, no exceptions, no tail calls.
`Function.bind` is a fairly poor match to partial application because A) it will not execute the function if you provide enough arguments and B) it also binds `this`, meaning you have to pass NULL or some other value every time you use it. https://ramdajs.com/docs/#curry would be a better option, as JS' metaprogramming features actually do somewhat support partial application.
We shouldn't have to rely on loopholes to satisfy any hard and fast rules. A lot of writing good code is just being reasonable and explicit.
If you call a function to launch a missile, you'd expect that it's just going to launch a missile and do nothing else. So why are we so concerned that it contains side effects? What other option do we have?
Logging is a bit different. For many, logging is probably a necessity to allowing you to look back and and understand context when things go wrong. But when you're programming functionally, I find that it's much easier to get a grasp on things compared to procedural code, which can become spaghetti much quicker. I also find that bugs are much less common, or otherwise much more obvious in functional code, negating the need for logging in the first place (for functional code).