It's definitely good practice for devs to learn some of the pitfalls that FP prevents and solves, but implementing it on a massive scale front-end application just seems impractical.
Having worked on a large streaming service and considering the author's 3 MONTH struggle after his 40 YEARS experience, I'd estimate that a re-write of our codebase there would have taken our 20 devs over a decade.
That's true if a team was trying to do so from scratch in js or ts. However React borrows a lot from FP and works at scale. A better example would be Elm.
I was using Clojure/ClojureScript around the same time. I’ve since worked primarily in TypeScript/JavaScript. I’m sure the FP experience influenced my opinion, but it seems impractical to me not to use FP techniques for a large scale frontend application. The applications I inherited and maintain now, which were certainly not originally implemented with FP techniques, have been gradually becoming much more maintainable as I’m able to eliminate or at least ruthlessly isolate mutations. Not only because the code itself is easier to reason about, but also because it’s easier to reason about dependencies between different parts of the systems—even parts which haven’t changed.
What that has done is made a whole generation of developers completely detached of the impact on heap allocation and GC. If web programs are slow and bloated, it's partly because of using nuggets from functional programming just for the sake of it.
Today, functional has won so completely that devs don’t even notice. Using classes is almost entirely antipattern. Factories with object literals and Claire’s reign supreme. Everyone prefers map, filter, reduce, etc over manual looping. Const and copying as a default immutability is preferred to mutation. Nobody thinks twice about higher order functions everywhere.
What do you mean?
the amount of time you're afforded for that exercise better fit neatly inside a very small window of implementation (in other words you're probably not going to do it, or at least do it justice)
>> figuring out which ones fit best to the system being built in terms of constraints
constraints will always get ya. It always ends up to being what is the tool/paradigm that you (and your project manager) are most comfortable with because you'll most likely use it in leu of anything else (especially given you are not the only one that has to be convinced -- engineers don't operate in a vacuum, and they love predictability, ie what they already know)
>> You won't get a very optimal solution by ...
YAGNI. Premature Optimization. KISS.
I am not saying that ^^^ is true, I'm just introducing you to your new conversation buddies for the foreseeable future. People always bring'em along for the conversation.
>>> trying to talk you into a religion
advocacy among unbelievers is always gonna come off like this, especially when the evangelists have dealt with so many naysayers and the apathetic majority. And this is probably the crux of the entire issue. Students' first languages in school are generally imperative languages. They are taught in their youth a path away from functional programming. Which is funny to me because my school was always promoting that their studies were there to help you grow one's intellect and not necessarily fit into some cog of the industry. But, I don't recall much playtime given to functional languages (not as much as they deserved at least).
My point is that it would be nice if FP was the first tool that was given to students that pour out of universities into developer teams. It would be nice if the easy button for a group was FP, not the other way around. Then it would be much easier to beat the other drums (like the testing drum).
>>> navigating all of the offerings, examining their trade-offs
>> the amount of time you're afforded for that exercise better fit neatly inside a very small window of implementation (in other words you're probably not going to do it, or at least do it justice)
Every project I have worked that has done a gap analysis has executed faster and smoother than those that didn't. A gap analysis being a more rigorous approach to an analysis of tradeoffs, solution capabilities vs requirements/existing software, and sometimes scoring/ranking (often with three recommended options depending on client's choices in tradeoffs).
> advocacy among unbelievers is always gonna come off like this, especially when the evangelists have dealt with so many naysayers and the apathetic majority.
If an advocate sees unbelievers and naysayers around it is more a reflection of the advocate tactics. No offense intended. Most developers will gladly hear an overview of how a new technology can make doing X easier, better or faster. If when there is a steep learning curve.
But if an advocate starts with "your tools are bad", "stop doing Y", "you need to unlearn Z" he is usually digging a hole for his technology, not advancing its adoption. If an advocate cannot produce powerful, convincing examples showing the goodness of his technology (without bashing existing tech) he should stop advocating and start learning. My 2c.
In other words, there would be little need to evangelize for FP if it was the base of understanding.
And while there are a lot that want to improve the field, I’d wager that most 9-5’s are tired, wanna finish a good days work and rest.
Let's say I run a CS department. And let's say that I've accepted that what my department is really doing is jobs training for software engineers. Well, what are their jobs going to be, imperative or functional? For most of them, for most of their careers, the jobs are going to be imperative. Why should we start with FP?
You could say "FP is better", but then we're back at the OP's point. FP is better for some things, and worse for others.
For you to say that FP should be the starting point, you need to show that FP is better for the majority of programming situations, or else to show that FP is the better way to learn programming for the majority of students. I have never seen anyone seriously attempt to show either one. (Some argue "it fits how we think better", but what they mean is that it better fits how they personally think, ignoring that different people think differently.)
Also, even though, as you’ve stated sw engineers tend to work in an imperative environment (which I’m arguing is an artifact of their formative years), junior sw engineers should at least start with a bit of trepidation to use that rope (if only in their heads).
Plus, utilizing a functional style (and understanding the whys of functional style, where pragmatic) would improve many aspects of industry (e.g reducing the friction of adding tests - did I mention that I love tests??)
Imho, FP has many tangible weaknesses, just a few off the top of my head:
- Immutability is non-intuitive: If I were to ask someone to make an algorithm that lists all the occurrences of a word on a page, they wouldn't intuitively come up with an algorithm that takes a slice of text and a list of occurrences, then returns an extended list, and an linked list with one more occurrence.
- Immutability can cause more problems than solve: If I were to create a graph of friendships between people, chances are that if I were to add a link between A and B, then not only A and B would need to be updated, but everyone who transitively knows A and B (which, since no man is an island would probably mean, everyone). This is highly complex, and probably just as bad as living with mutability.
- FP is not performant: FP code tends to be full of pointers and non-performant data structures like linked lists that have very little memory locality. It also makes optimizations such as updating things in a certain order to avoid recalculation impossible.
- FP has to deal with side effects: The real world has side effects, and your FP code is probably a bit of business logic that responds to a HTTP request, and fires of some other request in turn. These things have unavoidable side effects.
Also, there are FP langs out there that have better data/heap locality with indirected pointer data structures, because they don't share memory and so the pointers aren't tied to anywhere except where they are directly running.
I completely disagree with immutability being non-intuitive. Try explaining to a junior/bootcamp JavaScript or python developer why when you pass an integer into a function it doesn't reflect changes you made in the called function when you jump back into your calling frame... But if you do the same with a object/dictionary...
For junior programmers immutable passing is absolutely the default assumption and so I think it's reasonable to claim it's the intuitive choice.
There's even crazymaking shit where in python if you have a default array parameter you can seriously fuck up your model of what's going on if you mutate that array anywhere.
There is no intuition when it comes to programming. Everyone has to learn it. You’re appealing to familiarity of concepts you’ve already learned and assume everyone else thinks the same way.
There are highly efficient graph algorithms using pure immutable data structures. You don’t seem to be aware of how much sharing is enabled by immutability nor the algorithms for efficiently updating graphs in a pure functional way. Try reading this book [0].
Again there is a whole zoo of optimizations available exclusively to pure functional languages that you don’t seem to be aware of. Try reading Counting Immutable Beans. Some of the optimizations GHC can do are impressive. It’s not all bad performance.
Want to talk about cache misses? Check out an average C++ code base. It has a dedicated cache-miss operator.
Side effects are everywhere. And yes, in a purely reverentially transparent language, it becomes a pain being forced to be explicit about every mutation, network call, and file handle operation when you’re used to throwing caution to the wind and flying free with them. Improper handling of side effects include common use-after-free errors, etc. After years of dealing with this mud all of software, at least for me, explicit handling of effects becomes exactly what you want in order to keep your sanity intact.
[0] https://books.google.ca/books/about/Purely_Functional_Data_S...
There is no intuition in anything aside from the very few things we are born with. Everything else is learned from experiences.
And to that end, chances are someone has learned to follow procedure-like steps like a recipe at a very early age - indeed, in some schoolbooks recipes and other "procedural" works are among the first things someone does.
Children learn things like "to do X, first do Y and then Z" from a much earlier age, often before even learning how to read and write (e.g. via simple papercrafting in kindergarden) than something like "X = Y*Z". Giving and taking commands (i.e. "imperative" programming) as a concept is something people meet in their lives much sooner and are more exposed to than abstract problems.
Let alone adults with no prior training with programming.
I don't believe it's as natural as you seem to think.
What do you base this on? Did you run a study of people who had never programmed before and then asked them to invent a pseudo-programming language to solve this problem? Or are you talking about the "intuition" of people how have already learned to program, mostly likely in an imperative programming language, in which case your claims to "intuition" don't mean anything.
> - Immutability can cause more problems than solve: If I were to create a graph of friendships between people, chances are that if I were to add a link between A and B, then not only A and B would need to be updated, but everyone who transitively knows A and B (which, since no man is an island would probably mean, everyone). This is highly complex, and probably just as bad as living with mutability.
Firstly, that's not true, it depends entirely on what sort of data structure is used. For instance, a trivial model this functionally as a simple list of graph modifications, ie. type Action = AddFriend(a,b) | RemoveFriend(a,b), type FriendGraph = List(Action). Any change to the graph thus requires allocating only a single node.
Secondly, if you were to model this mutably, you would lose information about the fact that two people were friends at one time.
> - FP is not performant: FP code tends to be full of pointers and non-performant data structures like linked lists that have very little memory locality. It also makes optimizations such as updating things in a certain order to avoid recalculation impossible.
This argument is based on assumptions about the functional programming idioms, the compiler, the machine it's running on and more. I could just as easily say that imperative programming doesn't scale because you can't easily parallelize it across cores, and since single-core performance is now a dead end, even if everything you just said were true it's basically irrelevant for the question of performance.
> - FP has to deal with side effects: The real world has side effects, and your FP code is probably a bit of business logic that responds to a HTTP request, and fires of some other request in turn. These things have unavoidable side effects.
I'm not sure who denies this. The question is not whether useful software has side-effects, it's what sort of side-effects you actually need for any given program and how those side-effects should be modeled so you can properly reason about them and achieve the program properties you need.
If your programming language has pervasive side-effects then you have to deal with them everywhere, and the failure modes multiply. If your programming language does not support pervasive side-effects, then the side-effects are pushed to the edges of your program where they are more amenable to reasoning. Questions like "how did we get into this state?" are considerably simpler to backtrace as a result.
2. I just wrote the 'friends' example to make a point - common, complex programs often have huge graphs of interconnected objects, whether one would like it or not. Your solution is to build a journal of friendships and breakups - it's not a typical solution for object relations - seeing if 2 people are friends is a linear operation. Keeping the history around might not be necessary or useful.
3. Your FP code runs on a modern OoO CPU whether you like it or not - so it's safe to make that assumption. And multithreading might not be a silver bullet. FP does nothing to break long dependency chains. As for sharing values between CPU cores, considering the latencies involved, it might be cheaper to compute the value locally, but multithreaded optimization is a black art - it's not always obvious if sharing is better.
Another example is when I made a small toy Excel-like spreadsheet that supported formulas - while Excel itself is FP, I realized that the best way to update cell values, is to topologically sort all dependent expressions, and evaluate them one after the other - an imperative concept.
4. I was just making the point is it's easy to write a function in e.g. Java that is imperative on the inside, but is pure when called from the outside. Other languages will even allow the compiler to enforce this, while still being imperative. So both 'regular' and FP languages can avoid some side effects to some extent, but have to deal with others.
No, that's a purely functional algorithm. Note how he's just adding numbers to the end of a list and emphatically not mutating the existing items in any way. Tell me what you see as the functional real-world solution to this problem.
> 2. I just wrote the 'friends' example to make a point - common, complex programs often have huge graphs of interconnected objects, whether one would like it or not. Your solution is to build a journal of friendships and breakups - it's not a typical solution for object relations - seeing if 2 people are friends is a linear operation. Keeping the history around might not be necessary or useful.
The example doesn't make a point. In either imperative or functional programming, the operations you need and their time and space complexity will dictate the data structures and algorithms to use. Saying that programs have "huge graphs of interconnected objects" is not evidence of anything specific.
> 3. Your FP code runs on a modern OoO CPU whether you like it or not - so it's safe to make that assumption.
Make what assumption exactly? Microcontrollers are in-order. GPUs are in-order. You also made claims that FP programs are full of pointers and non-performant data structures. I have no idea what out of order execution has to do with this claim. Certainly early FP languages were pointer heavy, but early imperative programs were goto heavy, so I'm not sure what exactly you think this says about FP in general.
> And multithreading might not be a silver bullet. FP does nothing to break long dependency chains.
FP encourages and often forces immutability which does help significantly with parallelism and concurrency.
> Another example is when I made a small toy Excel-like spreadsheet that supported formulas - while Excel itself is FP, I realized that the best way to update cell values, is to topologically sort all dependent expressions, and evaluate them one after the other - an imperative concept.
Prove that that's the best way, and not merely the best way you know of, or the best way available to your programming language of choice.
They are clearly mutating the piece of paper to add new numbers to it. There is no linked list in sight. They are also flipping the pages of the book in order, another imperative paradigm.
The closest you could come to a pure FP algorithm expressed in physical terms would have been "Say there are still pages I haven't looked at, and say I have a stack of post-it notes on the current page; then, I would check if the word cheese appears on this page, and if it does, I would produce a post-it note with the page number on it, adding it over the stack of post-it notes; I would then flip one page; on the other hand, if I am looking at the last page of the book, I would add another post-it note to the stack if needed, and return the whole stack of post-it notes to you otherwise".
Imperative:
foreach page in book
if 'cheese' in page
write(paper, page.number)
Functional: search page:rest_of_book post-it-stack =
search rest_of_book
(if ('cheese' `on page)
then number(page):post-it-stack
else post-it-stack)
search [] post-it-stack = post-it-stack
Of course, we could write this easier with a map and filter, but I don't think there is any good way to express those in physical terms (not with a book - filters have nice physical interpretations in other domains though).No, they are simply adding a new record for the word occurrence. If the paper runs out, they grab another sheet and continue. This is clearly an append-only ledger, just like that used in accounting. These are both immutable abstractions.
The fact that this is happening on a single sheet of paper is merely an optimization, it's not a property of the underlying algorithm they're employing. The post-it equivalent you describe is simply a less space efficient version of exactly the same algorithm. You're basically saying that tail call elimination makes FP no longer FP.
> There is no linked list in sight.
What do linked lists have to do with anything? You don't think that FP or immutable programming have to use lists do you?
> They are also flipping the pages of the book in order, another imperative paradigm.
Traversing an immutable sequence in order is now an imperative algorithm? Since when?
What's really happening here is that you've already assumed that physical reality and people's intuitions are imperative, regardless of the contortions required.
This example of counting words and the general ledger are perfect examples: it's absolutely crystal clear that all of the recorded entries are immutable and that this log of entries is append-only, which is a characteristic property of FP and immutable abstractions, and yet you have to contort your thinking into looking at the paper itself as some mutable state that's essential to the process in order to call this an imperative process.
The person is describing the abstraction, not any optimization. If you were to translate their words directly into code, you would have to write code that modifies the piece of paper, because this is what they described.
In contrast, when using a persistent data structure, the abstraction says that I create a new structure that is the old one + some change, but, as an optimization, the computer actually modifies it in place. The implementation is imperative, but the abstraction is functional.
> You're basically saying that tail call elimination makes FP no longer FP.
No, I'm saying that there is a difference between writing an algorithm using tail-call recursion, and writing it using iteration; even if the compiler produces the same code. The tail-call recursive version is FP. The iterative version is imperative. How they actually get executed is irrelevant to whether the algorithm as described is FP or imperative.
In contrast, you're basically claiming that any algorithm that could be abstracted as FP is in fact FP. So, `for (int i = 0; i < n; i++) { sum += arr[i]; }` is an FP algorithm, as it is merely the optimized form of `sum x:xs total = sum xs x+current; sum [] current = total` after tail-call elimination.
> Traversing an immutable sequence in order is now an imperative algorithm? Since when?
Immutability and FP are orthogonal. Append-only ledgers are a data structure, not an algorithm; and it's algorithms that can be functional or imperative.
On the other hand, yes - traversing a data structure in order is an imperative idiom. In pure FP, the basic operation is recursive function application, not traversal. For loops and tail-call recursion obtain the same thing in different ways.
> This counting words and the general ledger are perfect examples: it's absolutely crystal clear that all of the recorded entries are immutable and that this log of entries is append-only, which is a characteristic property of FP and immutable abstractions, and yet you have to contort your thinking into looking at the paper itself as some mutable state in order to call this an imperative process.
By your definition, I understand this is an FP algorithm for producing a list of the first 100 natural numbers, since it's append-only:
xs := []int{}
for (i := 0; i < 100; i++) {
xs = append(xs, i)
}
You can certainly define FP = append-only, but that is emphatically not what others mean by the term. Instead, most people take FP to mean "expressing the problem in terms of function applications (and abstractions based on them), where function == pure function in the mathematical sense".> What's really happening here is that you've already assumed that physical reality and people's intuitions are imperative
I've actually shown what I think is actually an FP algorithm represented in physical terms, and how that translates 1:1 to FP code (and the alternative imperative algorithm). I don't think it's fair to accuse me of assuming that physical reality is imperative - at least not without showing your 1:1 mapping of the description to FP pseudo-code.
I can see the room for misunderstanding. My only argument is that FP is worthy of more playtime than it currently enjoys in undergraduate academia. I'd wager that the current percentage of FP/Imperative playtime is 95/5.
A friendship is really an edge in a graph. if you manage friendships by recording a collection of friend PersonId's on each person, you are modelling a graph by recording the same edge in two different places, and then hoping the programmer is diligent enough to never make a mistake and update the edge in one place but not another. If you model the friendship graph as a collection of id pairs, not encapsulated inside a specific person structure, this invalid state is no longer possible.
I'm not sure what you are on about regarding making transitive updates - that idea is obviously not going to work on any moderately sized network. Imagine if Facebook tried to record all your transitive friendships on your account!
Sure immutability is non-intuitive, but it's really helpful! Like borrow checking in Rust, immutability helps eliminate an entire class of bugs. And immutable code is often much easier to reason about, you need to keep significantly less of the context in your head.
Immutability may not be intuitive, but neither are pointers, hash tables, structured loops, OOP, etc. In any case, maintaining immutable code is certainly more humane than trying to reason about 10th order side effects. Finally, the majority of functional languages are immutable by default with optional mutation when needed.
Your immutable argument is a red herring. It’s the wrong algorithm. Bubble sort is much more intuitive than heap or quick sort, but you certainly wouldn’t recommend it.
You are flatly wrong about performance. Ocaml and StandardML are just as fast as languages like Go or Java. This is incredible when you realize These FP languages are maintained by a handful of people with basically no budget and are keeping pace with languages that have tens out hundreds of millions of dollars pushed into them.
FP doesn’t mean no side effects (though it does tend to encourage limiting and separating them). Outside of Haskell and Miranda, basically all the other FP languages have side effects and even Haskell has an escape hatch available.
Every program has effects. A program that does not have any effects (IO) would not be useful, as you can't get anything into or out of it. In FP we manage those effects, in order to help ensure correctness, with the additional benefit that good effect management makes the program easier to comprehend (nee reason about).
Contrast a procedural program with effects littered throughout the codebase, with a program wherein effects are pushed to the edges. In the latter, you and your team know exactly where all of the effects occur. Everything else is pure code: easier to test, easier to comprehend, easier to compose.
Category theory is not required for good effect management. It just so happens that monads like IO fit the problem space nicely; although the same could be achieved with a lazily evaluated definition of computation (i.e. a function).
> 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 never really a reason for anything, it's a personal attack on people who advocate the thing that you don't want to do. FP people are not bullying you, or shoving anything down your throat.
Have you ever worked with a FP evangelist? Every single experience I've had has been with a person who simply won't take "no" for an answer. The impatience, ego, and pettiness is bar none, really.
Aside from that, there are objective reasons to be skeptical: it's inefficient when it comes down to actual implementations that have to work on actual computers and solve actual problems ("but but tail recursion and copy optimizations lolol you moron you just don't understand it"). Also, most FP tends to become much more complex than an equivalent imperative program solving the same problem, usually through a terrible confluence of code density and mazes of type definitions.
Also, my dotnet contemporaries on average do not care as much about code quality as people did when I was in scala land. But this might just be a company culture thing.
That said, I'd never want to use lisp in a real production project. Same for FP. Get exposed to these things, take the learnings and bring them into KISS boring production code that is fast and maintainable.
I enjoy functional programming, but I'm a wizard with C# and .net, and I can pull a much higher salary continuing to focus there. I can write my own fun stuff in Haskell for myself and will continue to enjoy corporate money.
Meanwhile, FP is kind of a pain to connect things in, for most people.
That's why I try to juggle the least amount of variables as possible at the same time, while trying to have my code to read like a cooking recipe or a very simple literature. From that, FP feels almost natural.
Even more so, I think that most software bugs and spaghetti code is made by people that were too confident on themselves. They thought they could handle a lot of random unrelated variables floating around all the time, while having their functions do a lot of random algorithms at the same time.
And yes, they could handle it. The first time. But, in the long run, most people forget, or they leave and another person ends up having to maintain it, and that super brief-and-clever code became an unmaintainable mess.
Pure, immutable data structures might not be the right choice if you don't have the right language/tooling/runtime to take advantage of the benefits but they're there.
If it's so beneficial then those people can prove it by building something exceptional. I worked with Haskell and Scala for almost a decade and libraries were full of bugs and performance problems. A lot of bugs just hadn't been reported because so few people were using them. FP certainly has its strengths but so far there's very little evidence that it produces better software in the long term.
For example, I'm a biochemist with 12 years of education, and I can't seem to be able to convince anyone that vaccines are safer than no vaccines.
Humans just don't like new things or changing their mind, and that's a very generalizable fact, even among very smart groups of intelligent engineers.
Imagine trying to get people to accept vaccines, but in a world where we have other types of medicine that usually works well enough for all the things you've developed vaccines for, and the only argument you're allowed to make is "it's obviously better, look at how amazingly liquid the stuff in this syringe is".
You'll still get some resistance, but in our current world, pure FP is much more of a niche thing than pro-vaccine stances. There's a reason for that.
I suspect you aren't that knowledgeable about either.
> For one, vaccine people can actually explain why vaccines are good.
Unless you have a very particular background, I'm suspicious that you can actually follow the frontier of that argument. More likely, you're getting the ELI5 explanation.
> FP people seem to mostly just repeat variations on "it's easier to reason about" and showing trivial functions that are generally not easier to reason about.
Well-designed FP languages reify coherent low-complexity formal semantics. When FP advocates say "reason about", they mean in a (semi-)formal sense. No popular imperative language has any kind of denotational semantics, so good luck reasoning about the compositional behavior of C++ libraries, unless your definition of "reason about" is "think about informally".
People who actually understand complex things are able to provide ELI5 level explanations for people who haven't the background for more rigor. You're presenting a false representation of the OP's comment anyway: it never suggested the explanation for why vaccines are good required "frontier" level discourse.
> Well-designed FP languages reify coherent low-complexity formal semantics.
This is such a great example of what so many of us have noted about FP evangelists. It's so divorced from writing software as to be gibberish.
Yes, in theory.
In practice, people come to your explanation pre-conditioned with a lot of (often politicized) misinformation, Dunning-Kruger type overconfidence in their own ability, very little curiosity or openness to new ideas, and exhibit the attention span of a 26th percentile squirrel.
People tend to listen very little and are more skeptical of others than they are of their own understanding: instead of searching for ways in which their mental models need adjusting, they try to poke holes in your explanations. This undermines both "ELI5" approaches (because what you gain in simplicity you lose in nuance and correctness) and more pedantic approaches (which require more prerequisite knowledge, experience, and patience).
If you disagree with me because that is not your experience, just consider that you probably surround yourself with above-average intelligence people.
Until someone bucks the trend and does it.
But for that you need someone with actual intelligence and empathy; a Feynman of that sphere so to speak.
In every gatekeeper community this is the order of things until someone finally destroys the gates to the knowledge and the monopoly of the priesthood. Until then, it's basically "blame the victim" for their own lack of understanding (e.g. "people listen very little", "people are more skeptical of others", "they try to poke holes in your explanations", "They'll repeat whatever objections they've seen or heard somewhere", etc).
Hopefully people who read QED don't go around thinking that they know all they need to know to make an informed judgement about lagrangian QM.
And regardless of where we place the blame, at the end of the day:
> In life, there are a lot of obvious things that are very difficult to teach.
Only if the software you're writing is A) aggressively simplified, so as to be amenable to informal analysis, or B) garbage.
If you're only interested in writing garbage, or perhaps you don't even notice that's what you're doing, then the appeal of FP is significantly reduced.
Have you used other software? 99% of it is dogshit.
> libraries were full of bugs and performance problems
This is a really wild claim to me. IDK about Scala, but libraries in Haskell, Ocaml, and Elixir are all vastly better than libraries in C++, Java, Python, despite a smaller userbase.
Case in point for the problem with FP communication. When you speak in smug absolutes, it comes off as religion.
I'm pretty big into the FP world. I love Purescript. I dig Idris and exploring dependent types. I spend a lot of time at work teaching "functional" thinking. And yet, I still find most "hard core" FP evangelists insufferable for smugly explaining things as "obviously beneficial." ugh.
That is a lots that sucks about pure FP. It's not all upsides. The FP community can do a lot better at "evangelizing" by talking like engineers rather than sales people.
> talking like engineers rather than sales people.
They do talk like this - go to any Haskell conference and it's 90% engineering papers - much better than e.g. a JS conference. It's just that most people don't even know what PL engineering even looks like, let alone how to interpret it.
cmon fuckin guy
Seems easy to me.
If it's obviously benefitial, show me:
- Performance benchmarks showing how much faster the compiled code runs
- Case studies demonstrating faster development and/or fewer time spent debugging
- Studies showing consistently better maintainability
If it is lacking this data, it comes across as religious fervor.
The only semi-objective metric you've proposed is compiled code performance, which is a fairly small component of most people's utility function, especially when the difference disappears in all but numerical-computation-centric applications.
I am waaaaay less impressed by the half-assed "I borrowed a few paradigms and bashed them into an OO or multi-paradigm language but it's still fundamentally imperative" approach.
Reconceptualizing your entire program as consisting of recursion schemes and operations that use those recursion schemes, what I think the deep, true essence of functional programming as a paradigm is, is a very interesting and has a lot of interesting benefits hard to obtain any other way, along with some costs you might never otherwise think about if your mindset is too deeply into one particular world.
Rewriting a single for loop with five maps, reduces, or filters is nothing. It's a parlor trick. It buys you nothing of consequence. For avoiding bugs I almost never have, this requires a lot of extra syntax and costs huge runtime performance unless the compiler is smart enough to optimize it away.
That doesn't mean it's bad, per se. I use maps and filters as appropriate. In some languages, they're syntactically convenient, and if I'm mapping or filtering with something that is already a function anyhow, the performance issue is moot. I'm not saying this is bad and you should never use those things.
What I am saying is that is completely uninteresting as a "paradigm shift". Writing architecturally imperative programs with a light sprinkling of map and filter is nothing. Come back to me when your program is no longer architecturally imperative, and the top level of your program is now a two-line expression of a chain of mapMs and <*>s that represents your entire program. Now you have something fundamentally different.
Frankly, I think those who bang on about how vital it is to replace for loops with maps and filter chains have actually managed to miss the entire interesting point about FP. One could perhaps blame FP advocates for not explaining it very well with some justification. But it still is what it is. These people are banging on about how square a brick is, and how hard it is, and how nicely you can use one to bash things open, and how nicely you can polish up a brick if you want, but the point of bricks is that you can put them together to build walls. Sticking a couple of bricks in the middle of a mud hut leaves you with a weird looking mud hut.
The point of functional programming isn't map and filter; it is taking the class of things for which those are only two easy examples (and not even necessarily the most useful in general), and then building your entire program out of that class of things. You end up with something very different if you do that.
The linked article also bangs on about not having null references. Nothing stops you from having an imperative programming language that lacks null references. It is not a good or interesting example of why FP is interesting.
Anyways, upon re-reading, I lost a bit of focus on my original point, which is that you do need to watch out for which of the types of FP are being advocated. I have very different reactions between the two of "consider rewriting your fundamental view of the world to be composing recursion schemes instead of as the stringing together of imperative operations in various fancy ways" and "you shouldn't use for loops". You may not love the first; it is certainly a trip and not necessarily for everyone and everything, but it will at the very least expand your mind. The second is, well, mostly annoying to me because of the vigor of its advocates far out of proportion to either its programming utility or its mind expansion characteristics.
Thanks to closures, map/reduce function callbacks in Javascript are extremely handy.
Please link me to something like a stackoverflow answer with "vigor" greater than this, I'd love to read it.
You will be called names. You will be insulted and told that you just don't understand functional programming, probably because you're dumb.
I know this, because I had to deliberately cultivate a writing style that learned how to avoid this (which is why I start out waving around the fact that I don't just know a bit of Haskell, but actually know Haskell; I would not normally make a deal out of that, for instance). I wouldn't have had to deliberately learn how to write around this if it doesn't exist.
Now, the tradeoff I get is people who don't believe such advocacy exists, because it is no longer appearing in the replies to my posts like it used to. A worthy tradeoff even so, I think.
For me it’s difficult to immediately understand what you mean about the top level program, mapM, etc. I guess what you mean is that they entire program can be expressed as 1 call with some args? I feel like there is probably more nuance that I am not grasping.
I am curious to add this way of thinking to my toolbox, and I learn best by examples.
I have read things like Mostly adequate guide to FP and whatnot. They always get stuck on side effects and containers, which is fine, but doesn’t really address the larger scope of program design.
As a result of transformation, ~95% of the code is easily unit-testable and the only impure code is the call to main which basically starts all the domino falling.
FP advocates have a reputation for being zealous and pretentious. The only question is whether they’re right to be.
That is the kind of knowing-better-than-thou that undermines the evangelism.
It's in the runtime, effortless processes etc.
If all you can offer is "joy of programming in FP", you're in a cult.
I'd estimate there are three kinds of FP advocates:
1) people who have experienced personal pain in building or maintaining complex systems in imperative or other paradigms, and are genuinely astounded and relieved when we learn how Haskell and other FP languages can mitigate or eliminate that pain.
2) people who like to feel superior and rudely interrupt presentations to expound on monads and endofunctors and the like.
3) PLT academics who are just generally fascinated with building a mathematical programming language that can elegantly express the most advanced notions in math and logic.
Try to focus on and/or associate with #1 and #3 and ignore #2.
It is so incredibly widespread, it's not just "an anecdote of one person".
The entry-level courses at TU-Berlin where I studied had just been taken over by FP disciples when I started studying, and it was crazy. "Let me tell you about our Lord and Saviour Functional Programming, Hallelujah".
And of course the reality didn't come close to matching the advertising.
And it never does.
Another nice example was in a lecture by SPJ on parallel Haskell, where he says "This can only be done in a functional language.". Audience member: "We've been doing exactly this in HPC for decades. In FORTRAN". Instead of conceding or apologising for the gaffe, SPJ doubles down with something along the lines of "well, then what you are using is an FPL". Jeez. Oh, and when it comes to the results he reveals that the overhead is so high that you need 7-8 cores running full tilt to be equivalent to a single core C program. Jeez.
In general, there is also the widespread phenomenon I call "functional appropriation", where a similarity is noted between some aspect of FP and some other mechanism or paradigm or some such, and then the claim is made that this obviously means that the other mechanism/paradigm is just thinly veiled FP.
Newsflash: let me tell you about Turning machines. Or NAND gates...
For example FRP, "Functional Reactive Programming". Which is really just dataflow, and badly packaged dataflow at that. Or the React people's claim that the core concept or React is that the UI is a "pure function" of the model. Well, it turns out it is not. Not a pure function at all. And not really a function either. Just a mapping. And any UI had better be some sort of mapping of the model, or it's hard to see how it would qualify as a UI.
I could go on (and on, and on, and on...) but I'll stop now.
There are A LOT of developers, a lot of custom made software and a lot of web applications. And unless you really need performance, most web services are an ideal use case for FP practices. Even if you do it in plain Javascript.
Some people are zealots about it, yes. But imho FP is a better way to trickle down those concepts instead of telling people to spend decades to become imperative code masters.
That's one area it's well-suited for. Also in error-handling and prevention, and in refactoring, and configuration management. All personal pain points for me previously.
OK.
> a way of coding that's better suited to the current high level, highly distributed world.
But is it objectively better suited? One might even say "superior"? ;-)
Also, I actually believe FP is actually quite ill-suited to the high-level and particularly the highly distributed world.
A function is a mapping of an input to an output.
Can you provide a link?
I disagree. We are too tolerant with the zealots.
They are an obstacle to the exchange of knowledge. They don't want people to learn and get better at programming. They want to live in their own Mr Robot personal fiction.
FP is a tool. A great tool, yes. But a tool in the end. And we should promote it as such.
1. There's no dichotomy between FP and OOP. Sprinkle it into places where it makes sense to you. Adoption can come by degrees.
2. Just thinking in composable, pure functions on projects buys you serious mileage. You don't have to wrangle monads (or worse, victimize team members with advanced FP ideas). Just KISS.
FP often feels to non-practitioners like a pretentious and byzantine fad. We'll have better luck with widespread adoption if we can be inviting and dispel those negative associations. We'll seem less zealous if we can frame FP as a practice to dip one's toes into, rather than a religion to be submerged & baptized into.
Ultimately, some real-world processes are just procedural, and procedural languages are easier to apply to them. When it comes to callbacks, mapping functions to a list, etc., it's pretty clear to me that FP paradigms are superior.
In other words, I think it's part fad/religion, but as with anything, you can learn from it and take the good parts.
What remains when people advocate for more FP is the more esoteric stuff. (And non-nullability, which inexplicably gets associated with FP surprisingly often despite being orthogonal to everything else in the paradigm.)
At some point people try to save time and stick with practices that stand the test of time.
This is exactly where I was a called a zealot and gave up because it was obvious the other person felt attacked, as if I told them "you are an idiot for using procedural and imperative style all your career" -- which I absolutely didn't say or even imply.
Ultimately, selling someone on FP is selling them a new mental model and I think a lot of us how hard that is. People by default don't want to change the way think, it's a fact of life.
First it greatly depends on what "new project" mean here. There myriad of new projects within existing code base for which the existing used technologies are the major factor.
When it’s about creating a brand new product out of a blank slake, sure Cobol won’t be your first idea for a startup. Maybe Ada might come to mind if some high level of reliability is required, like in aeronautics and the like.
It also depends on the team skills. If you have team mates which are all Haskell advanced practioners, surely imposing J2EE for the next big thing might not be brightest move in term of crew motivation.
I'm not considering Erlang either.
I think one of the major reasons is because IO doesn't really fit well into the FP paradigm. All the theory and niceties take a second place when you find out that something as simple as "print a line here to console" isn't as simple as you thought it should be.
I disagree. One of the most significant benefits of FP (and pure FP in particular) is being able to deal with mutable state safely in concurrent programs. This is essentially the core problem which effect systems try to solve.
And I say that as someone who is very much fore “practical FP” — I do think that local mutability is sometimes simply the better fit for a problem.
I mean, that's precisely the point I'm making, that IO doesn't fit into the FP paradigm and that languages that make IO easier necessarily deviate from it. And once you bring non-FP concepts into an FP language, most people will reasonably question why do that instead of the easier way, which is bringing FP concepts into imperative languages.
From how when you console in to a cluster and run a command on another node, the standard io is redirected to you, to how tcp/up and up sockets are managed by the stdlib, it's really hard to beat Erlang.
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.
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.
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.
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.
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.
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.
Eh, I doubt it. I've encountered a few Haskell snobs in my time, but most people that I know who use FP do so completely silently and will discuss the merits and (and challenges) with you freely. I think the real issues with FP are lack of good tooling, package management, and being a major shift in thinking for most developers. It's a common theme for someone to say that learning FP (seriously) was the most impactful thing they've done to improve their software chops.
> The other major impediment is gatekeeping
??? what? I guess you could argue that pure vs non-pure functions are gatekeeping, but there are absolutely legitimate benefits to pure functions that basically everyone can agree on.
And it's not just the statement. It seems to me (from my outside perspective) that category theory is often used in a gatekeeping way.
In contrast, take SQL. How much of the mathematical theory of relations do you need to know to be able to write SQL queries? Yes, it might help, but the SQL gurus don't try to drag you into it every time you get close to a database.
I've only really ever seen monoids referred to in Haskell. If you actually read my comment, Haskell is not all FP (no PL is). It's not even a significant portion of FP. So just don't use Haskell, it's hardly the first FP language I would reach for and it's probably not the one you should either.
SQL DBs and the precise semantics of SQL are somewhat based on relational algebra, but SQL syntax is definitely not. It's also not using set theoretic terms in general, with the exception of UNION and INTERSECT.
It tells a lot how people go for common jokes to complain about a community. It's easy to fall for that if you never actually interacted with the people.
The saying "a monad is just a monoid..." is a cliché and an in-joke, not gatekeeping. It's the community having a laugh at itself.
> Nobody claims you have to understand category theory to write Haskell.
I've seem the claim that you can't really use Haskell without it, here on HN, more than once. (Or at least something that I interpreted as being that claim...)
Gatekeeping would be if the community said this with a straight face and everyone got impatient when you just "don't get it".
> I've seem the claim that you can't really use Haskell without [knowing category theory], here on HN, more than once
I've almost never come across this assertion; it's certainly not common in the community (or wasn't when I was learning Haskell). I can tell you it's certainly false: you can very well write Haskell without knowing or studying category theory.
Here [0] is an example:
> This module describes a structure intermediate between a functor and a monad (technically, a strong lax monoidal functor). Compared with monads, this interface lacks the full power of the binding operation >>= [...]
[0] https://hackage.haskell.org/package/base-4.17.0.0/docs/Contr...
These are the building blocks of Haskell. You learn them as you go, just as you learn what a "method" is when learning OOP. You don't need to know category theory.
Let's say I make a type class for Groups (in the abstract algebra sense). The rationale behind this is that there's an algorithm for exponentiation which is O(log(n)) versus the naive O(n) algorithm. So if you make an instance that's a Group, you get to use this fast exponentiation.
Sure, to understand and use this type class you have to understand what a Group is. However, I think it's a bit of a stretch to tell someone "in order to use Group you must first learn abstract algebra" because they'll think you're telling them to take a university level course. In actuality, they don't have to know much at all (they don't even need to understand _why_ the exponentiation algorithm works) - they just need to know what is a lawful Group.
The first day of the intro to abstract algebra course I took introduced much more information than you'd need to use this made up type class, and I expect the same of the others.
Like this is my understanding of Functors/Applicatives/Monads. I kind of know their shape and how to use them. If you asked me about any of the underlying math I would shrug and maybe draw a pig (https://bartoszmilewski.com/2014/10/28/category-theory-for-p...).
They never mean you have to learn Functor, Applicative, etc as used in Haskell and taught in every tutorial. If they did mean the latter, it would be a tautology, which is not very useful.
Compare "in order to do OOP you have to learn what an object truly is, its essence, and possibly take some courses on the philosophy of being" vs "you have to learn about methods and objects".
> It's like saying "you can learn how to do unions, intersections, and differences on collections of unique objects without understanding set theory".
You can totally do unions, intersections and differences without knowing set theory.
The years have proven that ease of programming and the burden of knowledge are the two most important elements of a programming language. FP zealots simply won't accept that their chosen paradigm is opaque to most for benefits that can't seem to be written out in human language.
On this website you will hear anyone say the wildest assertions. There's the whole human range of expression here. But what makes you think actual FP practitioners (and Haskelers) really believe this?
The proof wouldn't be what some random person here on HN tells you. The proof would be you getting involved in an actual FP community trying to write an actual FP project and being told that you just cannot do this unless you understand category theory. Which, as I said, is not something that happens... at least not in my (limited) experience.
Never confuse what people tell you here on HN, random forums or even throwaway StackOverflow comments with what actually happens in the actual communities when trying to achieve real goals and not just chat about stuff.
You just tried to badmouth whole community of FP programmers by taking serious a running gag, wow.
The original attribution of this line about monads comes from the (intentionally) comedic article "A Brief, Incomplete, and Mostly Wrong History of Programming Languages", published by James Iry in 2009: http://james-iry.blogspot.com/2009/05/brief-incomplete-and-m...
It is not a stance I have ever seen taken up in a serious manner by anybody in the FP community, and I work in PL academia (the region of the community from which I would expect to see the most snobbery). Please stop misrepresenting people based on a misunderstood joke.
There are people in the FP community who gatekeep and take on a snobbish tone — with that I do not disagree. However, their prevalence is generally overstated; it's a vocal minority situation. Most people I know or have talked to are very welcoming.
The actual quote that monads are monoids in the category of endofunctors comes from MacLane, and is intended for mathematicians.
Although the abstract phrasing can be traced to him, the particular use of "just" in the parent comment's quote tells me they're specifically thinking of the (deliberately condescending) version from the Iry post, especially since that's the version that gets memed throughout the FP community. After all, that particular phrasing is meant to convey a sense of "this is obvious and you are stupid if you don't understand it immediately", which is a far cry from the MacLane version (since that one is, as you said, intended for mathematicians).
But I probably ought to have included the full provenance regardless; thank you for bringing it up!
The original quote (from Categories for the Working Mathematician) is:
> All told, a monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.
Quite frankly, if you don't understand that sentence then you are in no position to judge whether the concepts could be made more accessible. Sometimes the important question is not "could this be easier to learn?" but "is learning it worth the effort?".
See https://news.ycombinator.com/item?id=33438320 for an example of this exact attitude.
That's exactly what worked for OOP—have we collectively forgotten just how hard OOP was pushed everywhere 15–20 years ago? Way more aggressive than any FP advocacy I've seen, and I've seen a lot. When I was learning to program every single book and tutorial pushed OOP as the right way to program; procedural programming was outmoded and functional programming was either not mentioned or confused with procedural programming.
I still have to deal with the fallout from OOP evangalism; I've had colleagues who unironically use "object-oriented" as a synonym for "good programming" and managers who believe design patterns are software engineering.
What's interesting to notice about this thread is how many messages are just oozing with smug superiority and disdain for anyone who doesn't share their knowledge. Yes, some are from genuinely humble and even-handed FP practicioners, but when we look at people who are vocal about FP, this small example shows around 90% of them in the gatekeeper camp.
This is a huge cultural problem, and one that I've seen many times in other communities over the years.
Take Linux, for example. Nowadays, it's trivial to get up and running with Linux, but it wasn't always so. Back in the 90s the installation was tricky, to say the least. The end user tooling was iffy at best and buggy as hell, there were tons of sharp edges, but most frustrating of all were the gatekeepers. People can handle challenges and pitfalls, but nothing quite takes the wind out of their sails like a smug asshole belittling them when they ask for help or express frustration.
Similarly with video codecs. In the 2000s when video codecs were a new thing, they had so many obscure options that almost nobody knew what would produce a good result, and so the gatekeepers came out of the woodwork to hold the sacred knowledge prisoner. They brought us byzantine software like the appropriately named Gordian Knot, and once again the forums were full of smugness, arrogance, and abuse as people despaired over how the hell to rip their DVDs into something viewable.
And it's similar in the Nix community, and countless others I've observed over the years.
In my experience, gatekeeping goes hand-in-hand with poor tooling and educational resources. The worse the UX, the more gatekeepers it attracts (because gatekeeping fulfills a need they have, so they flock to gatekeeping opportunities). Linux used to require an understanding of init scripts and crontabs and kernel recompiling and all sorts of esoteric knowledge just to get started. But now with mature tooling, it's easy to get started and then dig deeper at your leisure via the wealth of newbie friendly articles littering the internet.
Elixir is a functional programing language (It's not pure like Haskell or PureScript, but thats besides the point.)
The Elixir community is absolutely awesome! All or welcome.
There are soooo many resources for people to learn FP, the gate keeping thing may have been true ten years ago but I certainly don't think that the case today.
NB: This is not an invitation for discussion on the pros and cons, to berate either preference, or gatekeep.
There's a catch with FP, where many of their implications impose you to write better code. But it's not mandatory to go the functional way to do that, and I always try to explain the importance of those fundamental principles to other people, and they can try to implement it in whatever style they desire.
But when someone just doesn't want to learn, they always see me with a smug and gatekeeping attitude and I can't help but see them as people that just don't care at all.
IMHO, the ideal "future of programming" is like how a lot of game dev has C on the "back-end," and Lua "in-front."
Why one language or paradigm? Mix it up.
I love functional programming, but I doubt most companies that sell CRUD apps care about it.
Really a lot of it boils down to if this else that, and little more.
The point is to understand that "right tool for the job" and "C++ for everything" are religious positions, too. There might even be a dominant religious position, which likes to style itself as just the disinterested rational viewpoint.
The FP folks might be a somewhat more fervent (crazy mathematical) sect, but there's no getting away from religion, though we can call it by other names (fashion, "best practices", ...).
Pure functions excel at state management and reducing bugs. If there were a reason to make games with a functional language, this is the reason.
The big issues seem to be deterministic performance and resource usage. Garbage collection, lazy evaluation, etc all result in bubbles of weird performance. That doesn’t matter for the overwhelming majority of programs where the human limits in responsiveness is hundreds of milliseconds, but does matter it must scale into single digit milliseconds.
There are functional languages that have this capability, but outside of the partially functional Rust, they are basically unknown.
Pure functions excel at reducing bugs, I'll give you that. They also excel at transforming state. But managing it? Encapsulating it? No thank you. I'll take plain old classes over every state management idea that has ever been conceived for Haskell. Does anybody seriously believe they will manage the state for hundreds of thousands of different sprites, environments, bots, and players using the State Monad? I'd rather castrate myself. It's one thing to take immutable/functional ideas and code and use it in a stateful system, and another thing entirely to constrain 100% of your code so that it all fits in that neat theoretical box you've built for yourself.
Do you know what would be a better statement about the language in the context of games? A game engine. And actual games. That people want to play in 2022.
Have you ever looked over the codebase? It’s plenty large enough to draw useful conclusions from for most people let alone someone with his vast game experience.
https://github.com/id-Software/wolf3d/tree/master/WOLFSRC
Meanwhile, you are drawing bay conclusions with no credentials out evidence. As to actual games, setting aside the fact that Wolfenstein still sees play, loads of popular games are written in JS. Lots of others are in Java or C#. None of these make your case as Haskell, Ocaml, and StandardML (SML) are in the same performance range.
As to your argument about the efficiency of objects, what do you think functional languages use? Lets use SML as an example. There’s real arrays and they are also optionally mutable (yes, there’s linked lists too, but those can be used in C++ too).
Records are basically just C structs (they are immutable by default, but can contain refs which are mutable pointers). They can contain functions because functions are first class without the mess that many languages create.
You associate functions with datatypes which gives you the best part about methods. They also give you a kind of implicit interface too due to structural typing. I’d note that closures are mathematically equivalent to objects.
Finally, modules are everything a language like Java tries to get from classes (and more), but without any of the downsides of classes themselves.
People generally like the JS paradigm of factories and object literals (even if they hate the stuff like dynamic typing or type coercion). StandardML offers the same kinds of patterns, but with sound typing, simpler syntax without the warts, more powerful syntax, and performance in the same range as go or Java.
To me, your argument sounds like the people arguing that goto is better and more natural than looping constructs or the procedural guys arguing against OOP. I think if you messed around with StandardML, it would change your mind about what programming could be in the future.
Not once have I made an argument about the efficiency of objects oriented languages. Although that is important for performance wherever performance matters, it isn't my point in the slightest, and I already would agree with you that there are functional languages out there that are equivalent in performance.
My point is that the raw exposed complexity of functional state management (and especially pure functional state management) makes it worthless for game development. And until someone actually develops a real game, up to modern AAA standards using a functional language, all of your posturing is theoretical.
I can carry a pallet worth of goods to a supermarket using a pickup truck. It might be a little messy cause it can't pull up to a dock, so I'd have to unload it by hand instead of using a pallet jack, but the time I save by driving faster more than makes up for it. I could argue until I'm blue in the face about the theoretical benefits of using faster and more nimble pickups instead of slow and cumbersome lorries, but until someone actually builds a supermarket logistics network with them, those benefits are just theoretical. More importantly, the people who actually work in logistics industry would never attempt to do so because they can immediately see the problems with it.
So if you're that convinced your functional language would be better for game dev, then just do it already, cause nobody in the industry is gonna do it for you. I can guarantee it won't take long for you to understand what it is like when your paradigm choice puts you on the wrong side of the Expression Problem. And by the end of your experiment, you'll just be another crusty old insider that is too dumb and stuck in your ways to consider the amazing benefits of functional programming, just like what you're trying to do with me here.
The complexity in games is peanuts compared to any random business UI logic at whatever random company. Managing this complexity is an explicit reason to use functional paradigms. If OOP were better at managing complexity, why has everything been shifting from OOP to functional paradigms?
What about the functional approach makes it inferior at state management in your mind?
Maybe you’re thinking of FRP (functional reactive programming), but that isn’t even super mainstream among most functional programmers. I know some small games have used it, but it’s mostly used for specific kinds of UI in languages that enforce immutability and no side effects.
But yeah, for game development pure FP is probably a bad fit because you need pretty precise control over memory allocation and layout (from what I understand, never actually done any game dev).
reversible computing.
Ever since I read some stuff feynman wrote years ago about computing it seems like quantum computing and/or max energy efficiency would be achieved if information was not destroyed.
And I thought functional programming might be a programming paradigm to support this kind of thing.
The reason why multiple paradigms exist is because here in the real world, the competing issues and constraints are never equal, and never the same.
A big part of engineering is navigating all of the offerings, examining their trade-offs, and figuring out which ones fit best to the system being built in terms of constraints, requirements, interfaces, maintenance, expansion, manpower, etc. You won't get a very optimal solution by sticking to one paradigm at the expense of others.
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. (The other major impediment is gatekeeping)