If so, how?
- ReactJS: https://svitla.com/blog/functional-programming-in-typescript
- CodeMirror/ProseMirror: https://codemirror.net/docs/guide/
Another functional concept I find very useful is immutability. Immutability "clicked" for me after watching this talk[2]. I wish immutability was a first-class concept in JS/TS. (It will be, eventually.[3]) For now, I use immer.js for immutability in JS.
In retrospect, my programming naturally tended to be "functional-esque. Now I do it very deliberately, even though the language isn't purely functional (JS/TS).
[1]: https://hw.leftium.com/#/item/18043058
[2]: https://github.com/matthiasn/talk-transcripts/blob/master/Hi...
[3]: https://fjolt.com/article/javascript-records-and-tuples
The premise is this: you have two arms and two hands. They can be used in a controlled manner to do lots of useful things. They can also be swung around in a wild fashion that results in punching yourself in the face.
The concepts functional programming rallies against, like mutable state, are like arms: useful in certain circumstances, dangerous in others. But that's the thing: You don't have to write code in a dangerous way. You don't have to punch yourself in the face. That is to say, functional programming has reinforced my belief in the imperative style of programming.
Nowadays I think about computational requirements in terms of relations among behavioral dependencies. Like, "I want to perform operation O on input A and return a B. To do this, I'll need a way to a -> b and a way to b -> b -> b." I often pass these behavioral dependencies in as arguments and it tends to make the inner core of my programs pretty abstract and built up as layers of specificity.
Zooming out nearly all the way, it makes me feel tethered in a qualitatively unique way to certain deep truths of the universe. In a Platonic sense, invoking certain ideas like a monad make me feel like I'm approaching the divine or at least one instantiation of a timeless universal that operates outside of material existence.
I'd imagine some mathematicians might see the universe in a similar way - one where immortal relations between ontological forms exist beyond time and space and at the same time can be threaded through the material world by intellectual observation and when those two meet a beautiful collision occurs.
Through that I understood that the state of a program what causes most of the confusion and without variables reasoning about the code becomes much simpler. Without state everything becomes nice.
Through that I understood that state is the enemy, and global state is the greatest enemy of understanding. Later I realized that this same principle strongly aligns with testable code as more state is what makes code less testable.
It also forced me to use a lot of recursion which is nice as I understood it much better than in that single "Fibonacci number" example.
I also learned to write code that can safely run in parallel as functions without side effects and state are the most ideal candidates for that.
Smart doesn't work that way. Shame on me for not learning the same lesson from OOP. These are good technologies, but toxic culture undoes all the good and more.
End result: We can't have nice things, because the arguments are bogus.
Functional programming (similarly type systems) is another great technique to use when you have constraints that you’d like to be automatically enforced by the compiler (referential transparency, equational reasoning, etc.)
When I have to use a language that doesn’t allow expressing problems functionally when that would be the ideal expression of it, I feel I’m using the wrong tool for the job.
A good engineer understands the sweet spot of these tradeoffs. Use functional techniques with immutable data structures in the right places where performance is unlikely to become an issue.
For tree based processing functional programming Ocaml and Haskell are meant to be very effective. Especially if you can think of your problem as a functional isomorphism.
When I created my toy database I used a pipeline of tuples that were reduced. Relational data is easy to process with functional ideas.
Clojure pipelines are similar.
My problem with functional programming is its readability. If someone had a different idea of how they would approach the problem functionally, it can be tricky to read the code that performs the functional mapping.
A quick and dirty pipeither() with a "poor mans" either monad and you get a lot of the benefits of pure, testable functions, easy maintenance and debugging etc.
It's not Haskell, but its good.
[1]
const pipelineeither = function (l) {
const pipelineeither = function (v) {
let [left, right] = [null, v];
for (const f of l) {
if (left !== null) break;
[left, right] = f(right);
}
return [left, right];
};
return pipelineeither;
};
Having values and having your program phrased as transformations of values makes for straight forward, easily tested code. Provide inputs, assert outputs.
FP also taught me a lot about types, which allow me to define even better the parts that construct big programs.
bool SomeProperty { get; set; } = false;
string SomeValue => SomeProperty == true ? SomeValueA : SomeValueB;
string SomeValueA => "A";
string SomeValueB => SqliteConnection.ExecuteScalar("SELECT 'B'");
string SomeDescription => SomeProperty switch
{
false => "Extended description for false",
true => "Extended description for true"
}
string Output =>
@$"Result: {SomeValue}
Description: {SomeDescription}";
Combine with language features like LINQ and you can do an incredible amount of damage before you have to take out a proper method body.I've caught a LOT of bugs converting method bodies to expressions like this.
It is much more enjoyable working with immutable data structures which tends to remove long distance dependencies from your code. Instead, decisions are made locally and results passed up vs directly changing state in an object graph.
Not all problems are easily turned into a pipeline of pure functions, but it is worthwhile to learn which category of problems can.
Lean a Lisp. It will up your game.
What's an actual concrete use case of this? Where specifically would I use immutable variables to make my code better?
What was my frame for reference was piping input and output in bash programming and various declarative DSL like SQL or the Hashicorp language, functional programming in Clojure seemed quite productive and grokable, so I dove into that.
The best part is thinking in terms of data and transformations of that data, whether you compose the functions first or run data through a pipeline.
I've been applying this for some projects in a RoR codebase and the team seems to like it way more than others' more Ruby idiomatic but widely-varying styles. It wouldn't be nearly as useful without Sorbet static typing.
I was doing the Advent of Code in Rust and was disappointed to find that using for loops is substantially more convenient than functional constructions.
b) To me, functional programming was tremendeously useful when learning to program. Not sure exactly why, but it clicked with the way my brain works.
Composition is hard
It’s mostly things at this level, and not larger structural things. I don’t understand (and probably never will) the whole dance around using monads for side effects, aka the entire reason we run software in the first place. Your problem is “I want to update the database” and someone starts talking about stuff like “bind” and “shift”. Now you have two problems.
You just know deep down whenever you touch JS or some other language that there exists a better way of programming.