Not a program, but the concept of functional programming and pure functions. It makes it a lot easier to think about code, and also makes it easier to test and parallelize code.
Although be warned, it can also have the "side-effect" (functional programming pun!) of making you somewhat insufferable as you try to convince everyone around you that functional programming is amazing.
I'd be curious what your flavor of "functional programming and pure functions" is?
I ask, because I used to feel very similar about the OO paradigm (it helped me model the world easier, made me insufferable because of my zeal, etc). But, I had the privilege of doing that with nearly 20 years of Smalltalk. A battle worn path of ObjectiveC, Swift, Java, Python, Kotlin, and JavaScript later, I feel the pain. It's like being in forestry, one day you work with a chainsaw, and the next day they give you a fan with spoons welded for the blades.
These days I'm doing some Elixir... and I love it. I don't know how welcome it is in the "functional programming and pure functions" club, but I think it's awesome.
I have a working theory that what has made these paradigms loved or hated, is less about them theirselves, and more about the execution thereof. What made and makes both Smalltalk and Elixir appealing to me is their simplicity and straightforwardness. There's a mechanism you learn to reason about the problems you're trying to solve, and then you can excel at it, instead of constantly stumbling on edge cases where "hybrid" languages try to reconcile all of the paradigms together.
I had done a little bit of OO before diving into functional, but what really got me about functional was "the same inputs always produces the same outputs" and "no side effects"
In OO (the kind I've seen, and I've not seen Smalltalk, mostly C++), it's common for a member function to just go ahead and modify the member variables it needs to modify. This is considered "good encapsulation," but it's horrid for reading code and likewise horrid for parallelizing anything.
When you take that practice to the logical conclusion, you can end up with a member function like "void update();" (let's pretend that our class is a physics engine and this function presumably takes velocity and time step and increments position). Some early advice I got for writing maintainable code was "assume that whoever is maintaining the code after you is smart and in a hurry," and that sort of code just breaks that advice. If I see that kind of function, I need to dive into it to figure out what's going on. Whereas if I saw "state.position = update_position(state.velocity, state.dt);" I can make an educated guess about what's going on and move on.
I'm making myself a little angry here, so I'll wind down. These are the main points anyway, this sort of functional style feels like it takes something in 4D space where I have to twist my brain into a klein bottle to figure it out and puts it into 2D space where I'm like "oh, that's all there is to it then."
To your working theory, I see what you're saying, but I think I lean towards preferring poorly done functional code as opposed to well done OO code, because the functional code will be easier to test and shape into good code, whereas the OO code might get fragile over time. But let's be honest, it's not like "well done OO code" is an option we expect to have on the table :D
> I don't know how welcome it is in the "functional programming and pure functions" club, but I think it's awesome.
Erlang, and by extension Elixir, are very respected.
The fact that you have a strong background in Smalltalk isn't a surprise: Kay's idea of isolated objects sending methods to each other, independent of each other is OTP. Erlang, IMO, is the most beautiful combination of functional and (Kay's definition of) OOP there is.
You should check out rust. The compiler reasons about side effects + parallelization, and testing for you, but you get (nearly) complete low-level control.
As a bonus, you'll be able to write insufferable comments like these in HN threads.
I think he means two things, mind you I'm not very familiar with Rust, but I've done a lot of safety-critical C/C++, which is sort of like Rust
1. There's a mental cost on the programmer. You have to learn all the ins and outs of things like the "borrow checker" (a Rust concept) and it's on you to understand the rules and how to use them
2. It can slow down compile time. As your program gets bigger, the compiler is doing more work to check that it's valid code, so that will slow you down.
I was mostly thinking of the #1 mentioned by actinium226 in a sibling post, fwiw.
The main other thing I was thinking of was that GC languages sometimes have features that are very hard to support sensibly in non-GC languages: My favorite example is the higher-kinded types (HKT) which e.g. Scala and Haskell support. These can incidentally be used to "tame" side effects in a very principled way (via Monads). Another example would be lazy computation/values.
(Other applications of HKTs are validation integrated with parsing while still providing the maximum actionable feedback to users.)
Functional programming easily cost me hundreds of hours without much to show for it. Monad transformers, free monads, final tagless... this rabbit hole is very very deep.
No regrets though, would suffer through this again!
I haven't done any serious functional programming but I think the point is that overall it saves you time. It might take longer to write the program but you get fewer bugs, reliable caching and so on.
I know that with Rust the time-to-robustness is much lower than with Python for example.
It's so curious to me that functional disciples wear this as a badge of honor. In nearly all other facets of software development, we get badgered that it's better to produce code your entire team understands (code is read more often than written and all that) than code which only makes sense to you. If I ran around talking up the zen of single letter variables, my team would shoo me out of the room, and almost nobody on HN would applaud me for it. But for some reason, functional programming tends to get a pass, despite it just not clicking for a large swath of team mates.
> If I ran around talking up the zen of single letter variables, my team would shoo me out of the room, and almost nobody on HN would applaud me for it.
Yes, because single letter variables are bad.
> But for some reason, functional programming tends to get a pass, despite it just not clicking for a large swath of team mates.
Perhaps this is a problem with the team mates rather than with functional programming? I'm too dumb for functional programming, but I freely admit it's the superior way of writing software. If we were mathematicians rather than programmers, programming would be way better!
If you and half your team a not smart of enough for functional programming, along what dimensions is it clearly superior? Just like with single letter variables that only make sense to the author, if functional design only makes sense to one or two people on a team of many, it's clearly inferior.
Functional programming is superior because it leads to more correct programs. Classes of bugs become impossible. With dependent typing, you can prove even more about your programs and mathematically guarantee they're behaving to spec.
Instead, we as an industry choose JavaScript and endless runtime errors, because we don't care about the occasional blowups. We want to move fast, break things, and not have to learn maths.
Note the current state makes business sense: quick and cheap and who cares whether it works well, we'll throw it away soon anyway.
At my job I was introduced to functional programming with Python with strong typing.
I was at first rushing implementing ~50 business rules (which changed weekly) on the setup of a system. Then this functional wise kid came along and we did in one week what took me 5 weeks.
Along with that, using MQTT to separate your program with Protobuf makes everything so much easier. Program crashes? Let it burn and let systemd restart it. So much easier to test too.
Our code coverage is 99% if we exclude the main files that initialize everything.
Elm has easily saved me hundreds of hours per year in compile time alone, not to mentioned the likely thousands of hours it's saved me in time spent debugging. I still shudder to think of the horrendous compile times I got from even small TypeScript projects, and the days I'd waste trying to debug them.
Although be warned, it can also have the "side-effect" (functional programming pun!) of making you somewhat insufferable as you try to convince everyone around you that functional programming is amazing.