My angle was narrower: static types + ADTs improve the engineering loop (refactors, code review, test construction) by turning whole classes of mistakes into compiler errors. That’s not “what FP is”, it’s one very effective reliability layer that many FP ecosystems emphasize.
Even purity is not something exclusive to FP, D and Nim also support separating pure from impure functions. And if you ask me, the reason not many other languages have support for that is that in practice, it has been demonstrated again and again that it’s just not nearly as useful as you may think. Effects, as in Unison and Flix, generalizes the concept to include many more concepts than just purity and may perhaps prove more useful in general purpose programming, but the jury is still out on this.
In Unison, effects are called abilities, and they wrote a very good post explaining the differences!
https://www.unison-lang.org/docs/fundamentals/abilities/for-...
In summary: both have advantages and disadvantages. Which one is "better" depends on which factors you value more.
I also ship code super fast. When I find bugs I just fix them on the spot. When I find variables named wrong, I just rename them. The result that I often smash bugfixes and features and cleanup together and have a messy git history, but on the flip side you'll never find bugs or naming deceptions that I've left sitting for years. If something is wrong and I can reproduce it (usually easy in functional code), the debugger and I are going to get to the bottom of it, and quickly. Always and only forward!
Perhaps the most direct inspiration I took from there though was from the wonderful "opaque types" feature that Flow supports (https://flow.org/en/docs/types/opaque-types/) which for reasons known only to Hejlsberg and God, Typescript has never adopted; thus most people are unfamiliar with that way of thinking.
I’ve used languages with an approach like this. The difference in what I’ve used is that you separate the conventional part from the rest of the name with a space (or maybe a colon), then only refer to the value by the non-conventional part for the rest of the scope. Then the language enforces this convention for all of my co-workers! It’s pretty neat.
var intValue = 3;
…I separate the conventional prefix with a space: int value = 3;
…so now my co-workers don’t need to remember the convention – it’s enforced by the language.(I hope this makes the joke more obvious.)
User activeUser
…you’ll always know that `activeUser` contains a User value – something that might have an `Id` property. And the convention is enforced by the language, so it’s easy to communicate. These semantic distinctions are very useful, I agree.In dynamic languages, you are the type system.
This comment is either severe snark or severe ignorance.
If you remove passes of a compiler, you, looking at it whilst running, are the compiler.
None of this seems that new. Even people who write TS code still write tests and still ship bugs and still have to think about good architecture patterns, like the ones in the linked post.
This is important. I threw my hands up and gave up during the height of the Haskell craze. You'd see people here saying things like LISP wasn't real FP because it didn't match their Haskell-colored expectations. Meanwhile for decades LISP was *the* canonical example of FP.
Similar to you, now I talk about specific patterns and concepts instead of calling a language functional. Also, as so many of these patterns & concepts have found their way into mainstream languages now, that becomes even more useful.
Rust is actually aligned with the point: it delivers major reliability wins via making invalid states harder to represent (enums, ownership/borrowing, pattern matching). That’s not “FP-first,” but it’s very compatible with functional style and the same invariants story.
If the TS example came off as “types instead of validation,” that’s on me to phrase better, the point wasn’t “types eliminate validation,” it’s “types make the shape explicit so validation becomes harder to forget and easier to review.”
For example, using the input parsing scenario, a Java 1.0 tutorial in 1995 would have said that you should create a TimeDuration class which parses the input and throws an exception when given an invalid value like “30s”. If you say that reliability requires FP, how would you respond when they point out that their code also prevents running with an invalid value? That discussion can be far more educational, especially because it might avoid derails around specific issues which are really just restating the given that JavaScript had lots of footgun opportunities for the unwary developer, even compared to some languages their grandmother might have used.
Untyped FP languages can be productive, flexible, even elegant (I guess) but they are structurally incapable of expressing large classes of correctness claims that typed FP makes routine.
That doesn’t make them useless, just, you know. Inferior.