You’d expect the longest and most costly phase in the life cycle of a software product to be the initial development of the system, when all those great features are first imagined and then created. In fact, the hardest part comes later, during the maintenance phase. That’s when programmers pay th
Honestly, I doubt we’ll get all the way to pure functional programming, at least not in the next decades. But there is much to learn from FP.
Immutable-by-default is just sane. Algebraic data types offer a more explicit method for nullability and error handling. And higher-order functions for processing lists are so damn convenient, they’re basically included in every programming language now.
So, yeah, I think, we’ll take tools out of the FP toolbox and forget about some of the horrors of OOP, like massive inheritance chains, getters/setters etc., but ultimately stay on an imperative style of programming.
That will solve most of the pain and then that last step is too hard for the industry to take it.
I’ve been working with Clojure and most code is written using pure functional style. As a concrete example, Pedestal HTTP server has around 18,000 lines of code, and 96% of it is pure functions. All the IO and side effects are encapsulated in the remaining 4% of the code.
This has been a common scenario for the vast majority of Clojure programs I’ve worked on in the past decade. Core of the application does data transformations using native Clojure data structures. This is where all your interesting business logic lives. The interop lives at the edges and typically used for stuff like database drivers, HTTP servers, file access, and so on.
And I completely agree regarding immutability being the sane default.
I find that there are two major problems with OO. First is that it takes a lot of effort to learn how an API works because you have to pass object graphs around, and each object is a unique snowflake in terms of methods and behaviors it has. So, you often have to learn how dozens or even hundreds of objects are intended to be used.
Second, is that each object is effectively a state machine, and your application is structured as a graph of interdependent stateful objects. This makes it effectively impossible to reason about any non trivial application because all these relationships are too big to keep in your head. The only thing you can do is fire up a debugger get the application in a particular state and inspect it. Unfortunately, this is just heuristic since it’s hard to guarantee that you’ve covered all the paths leading to a particular state.
Meanwhile with FP library APIs become data focused. You call a function, pass it some data, get some data back, and that’s all you need to know. Data is both transparent and inert in nature. You don’t have any behaviors associated with it, and you don’t have to know what methods to call. The scope of information you need to keep in your head is drastically lower because you’re able to do local reasoning.
As an FP-fan interested in Clojure, how does one track if functions are pure in Clojure? I had assumed this was not possible, due to it not being statically typed (although I gather there is 3rd-party support for gradual typing).
There is nothing stopping you from making an impure function, but the language naturally guides you towards writing code that’s composed of a bunch of functions that transform data. Clojure also has explicit containers for mutable data and those have their own semantics, so you can’t accidentally cause mutation.
It’s also worth distinguishing between different kinds of side effects. One type of effects is doing things like logging, which in my experience are generally harmless. The type of side effects that I tend to worry about are the ones that couple functions together via mutable references.
And yeah, Typed Clojure library lets you do gradual typing, but it hasn’t really caught on with the community so far. Most people prefer using runtime contracts with Spec and Malli.
Thanks - I was just wondering how this somewhat precise statistic was obtained.
Otherwise, all that makes sense generally, though I tend to model logging as an effect in statically typed languages with effect systems. But I agree that it isn’t the most important thing!
I’m not sure how the statistic for Pedestal was obtained, don’t recall if the talk mentions it or not. There are static analysis tools for Clojure like clj-kondo that can provide these kinds of insights. You could see what parts of the code are pure based on what functions get called for example.
Sorry - I didn’t realize your original link was to the video and not the project. After watching it (good talk!), I found the clip in question. It seems like he is just saying that 96% of the codebase is functions (of any sort), not that 96% of the functions in the code are pure.
OK yeah, I misremembered the exact phrasing. You’re right he doesn’t say the functions are pure necessarily.
I don’t doubt that it’s possible to use FP for everything, I’m just pessimistic about our whole industry adopting it in the near future. It has frequently taken decades for the mainstream to catch onto new developments.
Oh yeah I completely agree that FP is nowhere close to being mainstream. Lots of mainstream languages are bolting on functional features, but there’s a big gulf between doing that and using an actual FP language. That said, there are lots of places where you can use FP professionally, and it’s not too hard to find a job where you can work with a language like Clojure. My experience with Clojure in particular is that demand for devs is higher than the amount of people who know Clojure, and that translates to higher salaries and better perks.
Which industry?
Programming is more than just writing Yet Another CRUD-backed data-siphoning web app. It’s more than supercomputing/high performance computing. It’s more than spreadsheets and word processors. It’s more than games.
Any one of those fields I mentioned are effectively a completely separate industry from the others. And I haven’t even touched the 500kg gorilla of embedded systems that outnumber all of those put together by an order of magnitude or two.
Some software industries (the web stuff, say, or user-facing software like spreadsheets and word processors) will find pure functional or major steps along that path to be useful and an improvement over current techniques. Others (high performance computing, games) will find themselves stepping backward if they go the full-functional route. And that 500kg gorilla I mentioned simply can’t use functional programming. (Hell, I don’t think there’s a pure-functional runtime that fits into any of the kit I work on, not to mention the runtime plus an actual application. I’m willing to be corrected, though: if there’s a functional language out there that will let me write non-trivial applications that fit in 256KB code space and 48KB dynamic data space, I’m eager to hear about it!)
However much you think you know about software development, be aware that it’s far larger than you think. (This applies to me too. I haven’t even glanced at financial or health software’s direction, and only have very vague notions of what aerospace systems entail.) And as a result, different tool sets are required for different problem domains. Functional doesn’t fit them all.
I generally take your point, though I believe FP can be applied to most domains with some benefit - it is just that existing, prevalent FP languages may not always be well suited for the job. In HPC for instance, there are a few interesting options:
For both games and HPC, Futhark may be of interest: “Futhark is a small programming language designed to be compiled to efficient parallel code. It is a statically typed, data-parallel, and purely functional array language in the ML family, and comes with a heavily optimising ahead-of-time compiler that presently generates either GPU code via CUDA and OpenCL, or multi-threaded CPU code.”
Sadly I can’t find it right now, but there was research language designed with the idea of separating the implementation from the specification, in such a way that the implementation could still be verified to conform to the specification; the specification was much more than a typical function signature as I recall. Basically you would write the function specification in a functional style, and then be able to have multiple implementations (e.g. for different hardware) conforming to that specification. I want to say this was from Standford but may be wrong about that.
Futhark is of interest as a future direction, chiefly as a supplementary language for sub-pieces of a larger, performance-intensive program. Note that its creators, however, explicitly state:
This is not a negative point, incidentally! I personally use a lot of languages in my work because I find it’s better to use a tool honed to near-perfection for a particular use case than it is to employ another tool that does something not quite the same with lower quality. I wish more programmers learned more tools so they stopped doing the programming equivalent of hammering nails with a large wrench.