85 karma · joined January 28, 2021
For foldr, I mean that specific implementation - which fully prioritizes performance and uses several levels of unwrapping, etc. - is hard to reuse for functions we wish to maintain. Using List.foldr is fine (when applicable, which is not always), but copy-pasting its implementation to adapt it to our need isn't, from a maintenance point of view.
On the topic of continuation-passing style, I don't personally believe that the solution I've highlighted uses CPS. At least from my shallow understanding, CPS uses functions, which my solution doesn't. CPS can easily emulate TRMC, but TRMC works differently (and is more performant though is applicable to less situations).
My understanding of CPS is maybe shallow, so I could be missing some understanding, but to me they're different and therefore not part of my explanation.
> Continuations provide the structure to hold your "holes" until they can be filled in
That's one mental model to view it, but there isn't actually any "real" hole to fill with CPS, whereas with TRMC there is (at least for data construction).
The only difference is List.foldr's implementation is more performant (up to a certain number of elements as you say). If this is not what you asked, then I did not understand the question.
If you didn't have a keyword, then all functions would have to be made stack-safe, which is I think a lot to ask the users in some cases, especially when the language doesn't give you all the tools for it (I think CPS does not work for instance in Elm, currently investigating that a bit).
If you have a keyword, then it would be as restrictive as you'd want it to be I imagine?
As for walking a tree, I agree, but this optimization does not work well with trees shrug, making lists a nicer example in my opinion.
As a teaching method, I feel that factorial is a relatively good example because it is fairly easy to grok in your head or on paper. Recursing through a tree adds a lot of mental overhead, so I feel like it could be a follow-up example to factorial, but it's probably too hard as an initial example. Also, switching to a stack-safe function using recursion is again much easier for factorial than it would be for traversing a tree.
I myself personally tend to run elm-review when I'm ready to make a PR most of the times.
Refactoring code or updating dependencies is and feels a lot safer in Elm than in TS, and doesn't require asking for every npm package author to add TS type definitions.
I'd also argue that TS/JS have the esoteric/cool features. Elm is a very simple language, complete enough to be able to write most programs, but small enough to give you a lot of guarantees about how the code will behave.
Elm may not be a good fit for every project out there, like when you need tight integrations with specific JS projects, but it will work well for most projects.
At this point, I'd personally be more interested in hearing about cases where Elm was NOT a good fit (and why) rather than where it is.
But even if you do blindly accept each fix, the behavior of the program won't change.
so instead of
function a(b) { assert(b > 0); return b + 1; }
you'd do
a b = if b > 0 then Ok (b + 1) else Err "b was not > 0"
To then use the result of the function call, you'll need to unwrap the result, by handling the case where the function returned Ok, but also the case where you return Err
case a -2 of Ok b -> -- display the value b Err errorMessage -> -- display the error message
THis is the kind of technique Elm uses to ensure that you'll have no runtime errors in your code: by forcing you to handle the error cases. The nice thing is that the types can indicate whether something can fail or can't.
then he (I) happily switched to Elm, because even with a lot of static analysis rules, it's really hard to avoid impure functions when a lot of the libraries and existing code use that, and sometimes even require that.
is talk called "Effects as Data" describing that idea: https://www.youtube.com/watch?v=6EdXaWfoslc
It's not that it's impossible, but that 1% (or 5%, 10%, ???% depending on how the project is structured and whether it uses a lot of side-effects or dynamic properties) can be enough make your program crash if the assumption turns out to be wrong.
Another example that you could go for, is can you determine whether a property of an object is used or not. If you have a language like JavaScript where you can do `object[propertyName]`, that turns out to be very hard. In a pure functional language, that is comparatively pretty straightforward to detect.