> One of the big reasons why FP languages have so little penetration is because the advocacy usually feels like someone trying to talk you into a religion.
This is a part of it. The other part is FP's a bit of a mind fuck if you're used to procedural programming. Take a classic example: Haskell. To do anything remotely productive it's advisable to understand how monads and the various monad design patterns fit together. This difficulty is further compounded by monads having it's foundation in category theory, a branch of mathematics. But hey, they're just monoids in the category of endofunctors, it can't be that hard ;) You can rely on `do` syntax a lot but it really helps to learn how they work.
> I immediately distrust any article that makes sweeping claims about one-paradigm-to-rule-them-all.
I get where you're coming from but this is the end state as far as I'm concerned. Pure FP is where we're all headed. I am convinced the more we try to make mutable state, and concurrency safe and correct, the more FP concepts will leech into future languages.
Rust was one of the first steps toward this. The language is procedural but a lot of the idiomatic methods of doing things are functional at heart. Not to mention most of it's syntax constructs return values. You can still mutate state, but it's regulated through the type system and you can avoid it if you really want to.
Rust's enums, arguably one of it's killer features are algebraic data types and they're combined together the way you would use monads in Haskell. This not only avoids null typing (if you ignore ffi), but also provides a bullet-proof framework for handling side-effects in programs.
I could be totally crazy, but I reckon many years from now, we'll joke about mutating state the same way we joke about de-referencing raw pointers.
> I get where you're coming from but this is the end state as far as I'm concerned. Pure FP is where we're all headed. I am convinced the more we try to make mutable state, and concurrency safe and correct, the more FP concepts will leech into future languages.
Having been around the block to catch a couple "FP will rule the world" cycles I can say this is likely untrue. While FP is useful and I personally enjoy it, it is not always the most efficient, nor is it the most clear. This is true in your example of Rust as well. Functional constructs are often slower than their procedural counterparts. For example, having to pass around immutable data structures becomes pretty memory intense even with a highly developed GC.
> I could be totally crazy, but I reckon many years from now, we'll joke about mutating state the same way we joke about de-referencing raw pointers.
Dereferencing raw pointers has been joked about as long as I've been in the industry, and probably even joked about in the 70s. We still dereference pointers today. Similar, state is a very natural thing to reason about. Sure, you can argue as many FP fans do that stateful programs can be rewritten with pure functions. I have yet to see FP code that does this that doesn't negatively effect readability. Even something as simple as large map/reduce/filter chains can quickly become extremely difficult to debug when compared to a very simple loop.
All this to be said there's a lot of benefit many languages can take from FP paradigms. Map, reduce, etc are great examples. Offering immutable state, and algebraic data types could also be beneficial especially in the areas of concurrent and parallel programming. In my professional experience the problems usually start when you begin talking about pure functions which in theory are awesome but sometimes don't map to a problem domain well, or become extremely hard for Joe Developer to get used to. Often times I will think in a functional way, but rewrite things into a procedural way, because communicating your idea is often just as important as the code you write.
> In my professional experience the problems usually start when you begin talking about pure functions which in theory are awesome but sometimes don't map to a problem domain well, or become extremely hard for Joe Developer to get used to.
It's a ripe field for research. I do wonder if there is a better way to program in a pure functional way that conforms to the impurity of the real world. I agree with you though, I think while pure FP is a tantalising abstraction, it must obey the the whims of the hardware. The only way you're getting pure FP to the absolute bottom is if you can monadically formalise the hardware you're running on, which has been attempted [1].
> Often times I will think in a functional way, but rewrite things into a procedural way, because communicating your idea is often just as important as the code you write.
I think this is perhaps the common benefit that is cited from learning FP. It provides an alternate way of expressing the same solution, but maybe in a simpler fashion. The reverse is also true, what is not straight forward in FP may be expressed simpler, procedurally.
> The other part is FP's a bit of a mind fuck if you're used to procedural programming
It's not "if you're used to procedural programming" - FP is simply hard for people to reason about. You can often very intuitively reason about imperative programs, while FP always requires putting your abstract thinker cap on. Look at any non-software person describing describing how to do something and see how often that sounds like an imperative program vs an FP one. Hell, most algorithms papers are themselves using imperative pseudo-code, not FP pseudo-code (unless they're specifically describing novel algorithms important for FP of course).
> I am convinced the more we try to make mutable state, and concurrency safe and correct, the more FP concepts will leech into future languages.
Concurrent mutation is fundamentally hard regardless of what paradigm you chose to implement it in. Parallelism can be made safer by FP, but parallelism is actually pretty easy in every paradigm, with even a little bit of care. And concurrent updates of mutable state are just a fundamental requirement of many computer programs - you can try to wrap them in monads to make them more explicit, but you can't eliminate them from the business requirements. The real world is fundamentally stateful*, and much of our software has to interact with that statefullness.
* well, at least it appear so classically - apparently QM is essentially stateless outside its interactions with classical objects (the Born rule), but that's a different discussion.
Eh, it really depends. Some people find trees easier to navigate through recursion than iteration, same for lists and graphs. Some do better using decomposition on a list to get a combined value or create permutations than doing it through iteration and indexing. FP commonly becomes problematic when things are far more trivial to do stateful than stateless, with side-effects than without, and more.
FP usually shines in small sections of easily isolated code, where it is still trivial to grasp for most and its strengths show more than its weaknesses. Given that code isn't more easily expressed using iteration, anyway. The moment things have to be woven together and not leave any side-effects is when the difficulty jumps to 11.
Sure, there are contexts where the functional approach is actually more natural - especially already highly abstract contexts. I actually like to use certain functional paradigms in my day to day programming (or did, before switching to Go). I just dislike the claim that imperative code is simply more familiar and not more natural/intuitive (in general).
> It's not "if you're used to procedural programming" - FP is simply hard for people to reason about.
It's relatively straightforward once you understand the patterns and idioms, just like procedural programming. `age |> add10 |> substract9` is about as readable as `add10(substract9(age)`, but we're just so used to the latter.
Using Haskell as the classical example of FP is a bit like using Rust as the classical example of imperative programming.
You can do it but they're much harder than most other languages in the class and not anywhere near representative of the whole class.
My FP journey was: Scheme (different but okay) -> Haskell (wtf) -> Erlang -> Clojure -> Elixir. Nowadays when I reach for an FP it's Clojure or Elixir, mostly based upon the problem at hand.
I’ve used Erlang, Elixir, and Elm and found them all quite easy to learn. There are different coding strategies, sure, but for me FP is much less frustrating than OOP.
This is a part of it. The other part is FP's a bit of a mind fuck if you're used to procedural programming. Take a classic example: Haskell. To do anything remotely productive it's advisable to understand how monads and the various monad design patterns fit together. This difficulty is further compounded by monads having it's foundation in category theory, a branch of mathematics. But hey, they're just monoids in the category of endofunctors, it can't be that hard ;) You can rely on `do` syntax a lot but it really helps to learn how they work.
> I immediately distrust any article that makes sweeping claims about one-paradigm-to-rule-them-all.
I get where you're coming from but this is the end state as far as I'm concerned. Pure FP is where we're all headed. I am convinced the more we try to make mutable state, and concurrency safe and correct, the more FP concepts will leech into future languages.
Rust was one of the first steps toward this. The language is procedural but a lot of the idiomatic methods of doing things are functional at heart. Not to mention most of it's syntax constructs return values. You can still mutate state, but it's regulated through the type system and you can avoid it if you really want to.
Rust's enums, arguably one of it's killer features are algebraic data types and they're combined together the way you would use monads in Haskell. This not only avoids null typing (if you ignore ffi), but also provides a bullet-proof framework for handling side-effects in programs.
I could be totally crazy, but I reckon many years from now, we'll joke about mutating state the same way we joke about de-referencing raw pointers.