What does HackerNews think of fantasy-land?
Specification for interoperability of common algebraic structures in JavaScript
[0] - https://ramdajs.com/docs/
[1] https://char0n.github.io/ramda-adjunct/4.0.0/index.html
[2] https://github.com/fantasyland/fantasy-land (A bit heavy on jargon)
Note there is a python version of Ramda available on pypi and there’s a lot of FP tidbits inside JAX:
3. https://pypi.org/project/ramda/ (Worth making your own version if you want to learn, though)
4. For nested data, JAX tree_util is epic: https://jax.readthedocs.io/en/latest/jax.tree_util.html and also their curry implementation is funny: https://github.com/google/jax/blob/4ac2bdc2b1d71ec0010412a32...
Anyway don’t put FP on a pedestal, main thing is to focus on the core principles of avoiding external mutation and making helper functions. Doesn’t always work because some languages like Rust don’t have legit support for currying (afaik in 2023 August), but in those cases you can hack it with builder methods to an extent.
Finally, if you want to understand the middle of the midwit meme, check out this wiki article and connect the free monoid to the Kleene star (0 or more copies of your pattern) and Kleene plus (1 or more copies of your pattern). Those are also in regex so it can help you remember the regex symbols. https://en.wikipedia.org/wiki/Free_monoid?wprov=sfti1
The simplest example might be {0}^* in which case we find:
“”, “0”, “00”, “000” … a language with only one letter in the alphabet could still make all the natural numbers like that.
With Boolean alphabet (two variant enum) we have {0,1}^+ which omits the empty monoid by using the + operator:
“0”, “1”, “00”, “01”, …
note this sequence will eventually yield every possible Turing tape, which is the same thing as listing every possible series of coin flip results assuming no coins land on their side (Turing’s undecidability paradox might be bullshit, but that’s another story)
I meekly suggest, FP can save you days of headache if the only thing you do is clone your data and mutate the clone. Yes, that’s inefficient, so is dealing with infinite bugs due to state mutation in multiple places. Also, you can look into HAMTs / immutable / persistent data structures to juggle the pointers and copy-on-write on the storage data structure if you find yourself repeatedly cloning data then it would accelerate your stuff.
Options include: Python: Immutables https://pypi.org/project/immutables/ Rust: RPDS https://docs.rs/rpds/latest/rpds/ and Im https://docs.rs/im/latest/im/
Rust isn’t great for letting you do FP things like other languages, but it does have the best type system imho which makes it the leading functional programming language right now imho. If you’re not using too many specialized python packages then I recommend using Rust instead, even for toy demos, as you can be more confident your code works without needing to run it and wait for a crash like you would in debugging python, and the tests also run faster in rust due to the incremental compilation. Use cargo-watch and you can retest your code every time you save your work.
https://github.com/watchexec/cargo-watch
I usually write a make command to cargo watch and rerun each test file : code file pair independently so then you won’t rerun your tests in other modules when you change the one you work on (faster but might miss stuff if you change API contracts which touch other parts of your codebase)
Thanks for the links. I know I've seen @gcanti's name a thousand times already, but had utterly forgotten fp-ts.
Transpiling, hooks and components have all been done (and done better) before React came along, but often they were laughed out as esoteric toys that have nothing to with "real programming". It's just hard to teach these concepts until you actually use them, and it's hard to get people to use these concepts unless they are included in something else that you need.
The same happened early with React, both JSX and VDOM were laughed at by many developers because "they looked stupid" to people who didn't understand them and were used to one way of doing things. MVC+jQuery+templates was the flavour of the day.
Same thing with TypeScript, FRP and so on.
Most likely, whatever concept is gaining momentum today, it's an exact or approximate copy, or in rare cases a novel remix of what has been researched 10-20 years ago, and used in production for 5 years before you heard about it on HN
These people who like to apply category theory etc. in programming have heard this mocking of their work as stupid esoteric fantasy (and then seeing them become mainstream later) so often that they literally called one of their specs "Fantasy Land" https://github.com/fantasyland/fantasy-land
There's still lot of work to be done. JavaScript doesn't even have proper immutable data structures natively, which would be a REAL low hanging fruit.
Which is to say, maybe start out in Typescript itself? There are a lot of great more functional paradigm-focused libraries out there in "Fantasy Land" [1] and "Static Land" [2].
A benefit to starting in Typescript as well would be that you could move to Elm or PureScript if there is time/interest and reuse many of the same libraries and/or even some of the earlier TS code/projects.
It obviously seems like a trade-off that using a language that isn't "strict" or "native" to the functional paradigm, students might be tempted to avoid learning the functional paradigm and fall back to old habits. But on the other hand, that can still be a useful escape hatch to avoid frustration, especially with High School students who might have enough other stuff on their plates that some leniency might be handy/warranted (and for High Schoolers they shouldn't have that many "old" habits to fall back on anyway). It could be good teaching praxis to start with "Why did you fall back to doing this imperatively/OO/what-have-you? Did you try X? What if you explore Y that we discussed in class?" You and the students might learn a lot from dialogs like that.
Just some thoughts. There's probably no "perfect" language, but sometimes the best language is the one people already know because that gets a lot of the basics out of the way and out of the focus.
Which then resulted in the creation of a specification called fantasy-land[1] as a form of mockery towards the "incorrect abstractions are fine" attitude. The goal of this spec is to standardize on functional algebra nomenclature in JS.
That's silly because the benefits of this type of programming can't be grokked until you use it on real projects.. it's honest-to-goodness not enough to look at small examples and judge whether it's worthwhile or not.
There's a lot of this realization going around actually - for example Rust's main raison d'être is basically: "look - we _can_ write safe c/c++, some of us can even nail it perfect every time in small examples, but in large apps it's inevitable that we'll all make mistakes"
Here's two issues with try/catch in real-world apps:
1. You aren't forced to write it. It's simply possible to forget that a function might fail (e.g. when that failure is defined as a side-effect). This is even worse when there's no type system forcing you to think about it. We all make this mistake!
2. You can't easily distinguish between expected errors and unexpected errors - and even where you can, dealing with that becomes a big mess.
Unexpected errors (like "out of memory", "computer is quitting", "electricity is gone") - _should_ crash and burn and stop the app (unless you explicitly have a way to recover from it - in which case it's actually an _expected_ error). Letting the app continue on where there's undefined behavior is dangerous.
Expected errors (like "user not found", "wrong password", perhaps even "network down") need to be dealt with. Every single time. Even in a function where it might not happen 99.999% of the time - that one rare time where it happens is especially hard to duplicate and fix.
That said, I agree with the comments that say it's a bit awkward in JS, and much less effective without a type system forcing the right constraints. I like using FP-TS[0] with typescript for extra help at "transpile" time, but Sanctuary[1] (based on FantasyLand[2]) with the runtime checking turned on is pretty incredible too.
With _some_ type system making sure that we're following the rules, monadic error handling is definitely more elegant and safer than try/catch.
[0] https://gcanti.github.io/fp-ts/modules/ [1] https://sanctuary.js.org/ [2] https://github.com/fantasyland/fantasy-land
https://github.com/jfmengels/eslint-plugin-fp
https://github.com/fantasyland/fantasy-land
That subculture is what I was referring to.
If you’re splitting hairs over such definitions without acknowledging the cultural aspect and use that as a hiring criteria I wouldn’t want to work with you either.
The discussion regarding inclusion in the stdlib is sensible, and I fully agree with @mhashemi's comments. See similar discussions, for instance, regarding dataclasses vs. attrs (https://github.com/ericvsmith/dataclasses/issues/19).
Sometimes, however, it's a bit annoying when something that you think should be widely available isn't included in the stdlib. The cons of course are:
- Fragmentation (several project pursuing similar objectives)
- Not being able to use a powerful construct to simplify code in the stdlib itself (the stdlib can only depend on the stdlib).
Regarding, for instance, functional programming, I maintain a list of interesting projects in this domain in Python: https://github.com/sfermigier/awesome-functional-python/blob...
There are at least half a dozen libraries that strive to achieve similar goals, and would probably benefit from being included in the stdlib (at least partially). It's hard to pick a winner among the list (like other in this thread, I went with toolz).
I believe that a way to solve (at least partially) this conundrum could be to work on some common specifications, like the JS community did with Fantasyland (https://github.com/fantasyland/fantasy-land), and then let projects implements the specification the way they want.
WDYT ?
[1] https://github.com/fantasyland/fantasy-land
[2] https://github.com/promises-aplus/promises-spec/issues/94#is...
I think I understood a couple words in that sentence, like "it" and "has". :P
Looking up fantasy-land, I found https://github.com/fantasyland/fantasy-land. I would like to better understand these monads everyone is talking about.
But, just to be super honest -- and possibly completely wrong -- the terminology is really off-putting. At a glance it just feels super complex, academic and completely divorced from practical coding. I get vibes of enterprise java class hierarchies looking at this stuff.
I hope I'm wrong, and I really am interested to learn more about it, but I can see from the OP's linked thread that I'm not alone feeling this way. It's not at all clear why I should be thinking about my JavaScript algebraically at all times, or what the practical advantages are. After decades of coding, I'm just getting more alergic to complexity, and this feels like complexity.
What are some good online resources that might help me change my mind and see the light?
On opinionated choices: I try to base that on mathematics. When it comes to async programming, that means equational reasoning and following some basic properties such as composition, associativity, left identity, right identity, etc, see https://github.com/fantasyland/fantasy-land . These would be neutral because math is often neutral. For instance, if intelligent aliens exist, they probably figured out the circle just like we did.
On ideological preferences: there isn't anything free of ideology, so yes I am motivated by some ideology, not unlike influential members of TC39. There, you'll often find opposition to functional programming ideas, even though JS was originally designed with influences from Scheme, and allowed functional programming better than, e.g., Java at the time. See these TC39 notes, for instance: https://github.com/tc39/tc39-notes/blob/master/es8/2017-09/s...
FP ideas are usually opposed because TC39 proposals are driven by concrete use cases, and FP is all about abstractions. FP ideology is that abstractions are good because they accomplish abstract goals, not a single particular concrete goal. That does not play well with the use-case-first process at TC39. So, ideologies.
On "it would hurt the ecosystem": I think by now a lot of people from different language communities recognize the success of RxJava/RxJS/Rx.NET, and it tackles all those complications you mentioned, but for even more complex use cases, because it handles multiple values over time. I'm not arguing that Rx is a silver bullet, I'm just saying the Rx community has first hand experience with solving and teaching solutions to the problems you mentioned in the second paragraph and it's nowhere near "catastrophic".