dependabot[bot] on npm_and_yarn
dependabot[bot] on npm_and_yarn
Bump nodemailer from 2.7.2 to 6… (compare)
dependabot[bot] on npm_and_yarn
Bump moment from 2.24.0 to 2.29… (compare)
dependabot[bot] on npm_and_yarn
Bump moment in /angular4-Bootst… (compare)
dependabot[bot] on npm_and_yarn
dependabot[bot] on npm_and_yarn
Bump karma from 4.0.1 to 6.3.16… (compare)
dependabot[bot] on npm_and_yarn
Andrew Ray updated his blog design and tweeted about it so I revisited and found this Flux pattern post (dated Nov. 13 2021, but I recall seeing it circa 2014). Have to say, understanding the SAM pattern helped me understand Flux (without Redux) this time around.
https://andrewray.me/blog/the-mental-model-that-helped-me-finally-understand-flux
In computer science, there is nothing more fundamental and intuitive than control structures. Every student learned about them in the first few weeks of any computer science program. We could not code without them, period. But they are not set in stone: we did get rid of the infamous GOTO in the 80s!
At the pre-ALGOL meeting held in 1959 Heinz Zemanek explicitly threw doubt on the necessity for GOTO statements; at the time no one paid attention to his remark, including Edsger W. Dijkstra, who later became the iconic opponent of GOTO.[3] The 1970s and 1980s saw a decline in the use of GOTO statements in favor of the "structured programming" paradigm, with goto criticized as leading to "unmaintainable spaghetti code"
And this paper is far from being the first call for using less conditional flows in programming. Even Uncle Bob has tackled the topic. In npm alone, countless packages have been published to provide an alternative. People tend to think that the catch with switch and if-then-else is that the more branches in your code, the more opportunities for untested and unexpected behavior. Every branch requires different use cases to be tested thoroughly, and even though, some tools, such as Istanbul, provide some indication as to how many branches have been executed, in addition to the traditional function coverages. But branches are part of the nature of programming, there is nothing we can do about it.
In this short opinion brief, I would like to give a new spin on when to use control structures and when to use some functional alternatives.
Ever since I discovered Dr. Lamport's Temporal Logic of Actions, my programming style has changed to become more explicit about assignments vs mutations of the application state, and more conscious about temporal logic. For me, there are three core patterns to programming: Object-Oriented Programming, Functional Programming, and Temporal Programming. A developer should master all three and use them accordingly, no one pattern wins over the other. We cannot write good programs with one pattern only.
One of the key notions of Temporal Programming is control state: when the light is off, and If the switch is in the off position, I can turn the light on otherwise check the circuit breaker (in bold we have the actions that are expected in a given control state). These are extremely familiar concepts we use every day, yet for some reason, we rarely make the control state explicit in the code we write. My take on it is that situation came about from a lack of alternative constructs, and the ease of use of switch and if-then-else, over the rigor needed to use temporal programming constructs, but that discussion is for another article. I'd like to focus here on something much simpler and actionable today.
First, we must absolutely stop using these control structures for (complex) assignments, it often leads to some pretty ugly code that is hard to read and debug. How many times have you written some code like this? come on, tell me in the comments...
How about a functional alternative?
My code is not new, it is actually heavily inspired by that article from Hajime Yamasaki Vukelic, which probably got inspired by many similar articles. I brushed up his code to make it a bit more developer-friendly and implement some automatic behavior.
We can now rewrite the code above in a functional way and do a proper assignment.
As a side note, my programming style uses named predicates such as exists, rather than writing inline condition functions. It makes the code a lot more readable, maintainable and testable for complex predicates (imagine a bug in a complex condition...).
The matchReduce function composes an array of functions conditionally. That may well be related to monads, but I have no idea since, to this day, I am still looking for a clear definition of what a monad is.
You can find some code examples on how to use match and matchReduce here.
Now we could stop here and say, alright, for all assignments I'll use this functional construct, and if you did that, that
const matched = (x) => ({
on: () => matched(x),
otherwise: () => x,
})
const match = (...x) => ({
on: (pred, fn) => {
const _pred = typeof pred !== 'function' ? (z) => z === pred : pred
const _fn = typeof fn !== 'function' ? () => fn : fn
return _pred(...x) ? matched(_fn(...x)) : match(...x)
},
otherwise: (fn) => (fn === undefined ? x[0] : typeof fn !== 'function' ? fn : fn(...x)),
})
const matchReduce = (...x) => ({
on: (pred, fn) => {
const _pred = typeof pred !== 'function' ? (z) => z === pred : pred
const _fn = typeof fn !== 'function' ? () => fn : fn
return _pred(...x) ? matchReduce(_fn(...x)) : matchReduce(...x)
},
end: () => (x.length > 1 ? x : R.head(x)),
})
if (x == 2 && y > 5) a = x + y
// a = match(x, y)
// .on( (u,v) => u == 2 && v > 5, (u, v) => u + v)
// .otherwise( (u, v) => u - v) // a = x - y
const p = (x,y) => x == 2 && y > 5
So I would not be concerned by that, in practice I have not seen it happening:
This could result in a long chain of actions that need to occur before the view is updated again