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.
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 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.
YMMV
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.
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
As a bonus, you'll be able to write insufferable comments like these in HN threads.
Also: The level of control offered by Rust comes at a steep cost compared to GC'd languages.
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.
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 know that with Rust the time-to-robustness is much lower than with Python for example.
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!
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.
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.