4 ms·
Don't 'ate 'em, just don't like 'em. Joking aside, 80% of the benefit of functional programming in practice, in my experience, comes from referential transpare
by netbioserror 2y ago
Don't 'ate 'em, just don't like 'em.
Joking aside, 80% of the benefit of functional programming in practice, in my experience, comes from referential transparency, expression-based programming, and sum types. It's even fine to allow for procedural code inside of these referentially transparent functions. The effects these features have on program structure, logic flow, and data structures are profound enough that the benefit of IO monads and other more pure features have diminishing returns for the extra elbow grease that needs to be put in. It's also fine to break the rules in a number of cases you can hopefully count on one hand; stateful code needs to be isolated and well-understood.
- eru 2y ago> Joking aside, 80% of the benefit of functional programming in practice, in my experience, comes from referential transparency, expression-based programming, and sum types. It's an interesting mix that you picked. Rust shows that sum types are useful even outside of functional programming. Just like Java (and others) have shown that garbage collection is useful even outside of functional programming; but functional programming was where GC was 'born'. Just like functional programming was where sum types were 'born'. Incidentally, garbage collection was born with Lisp. And Lisp is arguably a functional programming language (or family of languages), that for the longest time didn't have sum types. What we call functional programming is a grab bag of features and absence of some other features, but the exact contents vary over the decades. Even C++ got closures these days.. and that's how progress looks like!
- netbioserror 2y agoSorry, I take GC/RC as a given these days, but yes, the effect this alone has (not needing to write any memory semantic code the vast majority of the time) is revelatory. Especially for any language that compiles to a native executable. Huge performance for very easy-to-write and -maintain code.
- eru 2y agoOh, I agree. It's progress! Any new language these days either has to have automatic memory management (GC or RC), or have a good story for why what they are doing instead is different and worthwhile (like Rust). Or as a third possibility, your new language will get mercilessly mocked. We see similar developments for closures and support for first class functions in general. And my sincere hope is that in the future, other features like sum-types and pattern matching over them will go the same way. The functional programming community pioneered many of these features; but those features aren't restricted to that small corner of our industry. I hope that relations-as-datatypes will see widespread use in the future. SQL shows that relations are good for expressing business logic, and I used them in a Haskell dialect as an internal datatype (unconnected from any database), and the experience was great. There's no reason Python etc couldn't host relations as datatypes, the same way they already host dicts.
- greener_grass 2y agoexpression-based programming Yes! This is the real win of functional over imperative languages. It means you can understand blocks of code in isolation and then compose them into larger blocks. Bottom-up programming. This is what is missing from the imperative paradigm, and why that approach struggles to scale with problem complexity.
- netbioserror 2y agoSo very underrated. My program structure for years now has essentially been pyramids made of Lego-stacked functions, and I use value-returning syntactic features as often as I can.
- kalaksi 2y agoI'm baffled. You just described pretty much any programming language to me.
- netbioserror 2y agoI have personally seen production code consisting of C and PHP programs with GLOBAL shared state bounced back and forth between dozens of functions simultaneously, where the parameters reflect only a fraction of the actual input data to the functions' procedures. Expressions and airtight design may seem normal to you, but older generations of programmers either had little exposure to philosophical musings on the constitution of good software design, or received roundly awful early wisdom from the industry's nascence. Of course, C has a habit of enforcing essentially no rules on program design, so it was a wild world of scattershot practices (or anti-practices).
- greener_grass 2y agoIn most languages, the value of an expression depends on two things: 1. The right-hand-side of the declaration 2. Where you are in program execution Consider this JavaScript: let x = 1; x += 1; x *= 3; What is the value of x? Valid answers are 1, 2 and 6. Now lets restrict our JavaScript to pure expressions: let x1 = 1; let x2 = x1 + 1; let x3 = x2 * 3; The values of x1, x2, x3 are full defined by the RHS; they do not change once bound. This makes it easier to reason about the code - you only need to follow the definitions. This is an unnatural way to write JavaScript, but some languages make this easy (Clojure, Elm, OCaml, F#, Haskell, Erlang, ...).
- pmarreck 2y agoI benefit from functional programming without sum types in Elixir. You also failed to mention immutability. Immutability permits trivial concurrency without requiring mutex lock semantics. I'd call that a massive benefit, especially considering how much load a Phoenix server can handle.
- netbioserror 2y agoReferentially transparent functions naturally have immutable inputs and outputs, so that's implicit. But it's often useful to be able to write mutation-based algorithms within a function, so in that context I'm less concerned, so long as the size and scope of the function is constrained.
- pmarreck 2y ago> But it's often useful to be able to write mutation-based algorithms within a function I know. Quicksort comes to mind. But you then lose a guarantee. Might not make a difference practically, though (which presumably is what Rust's design banks on).