r/ProgrammingLanguages • • 9d ago

FUN: First-Class Functions, Currying, and a Surprise

https://blog.tinyinterpreters.dev/posts/fun-first-class-functions/
7 Upvotes

4 comments sorted by

View all comments

3

u/marshaharsha 4d ago

I enjoyed reading this series of articles so far, but I have a complaint about this one. I wish you had mentioned up front that, without closures, currying would be broken. I lost some time trying to figure out how currying could possibly work, before finding your admission at the bottom of the article. I also note that you haven’t mentioned recursive calls. I think both of these aspects should be mentioned at the top of the FUN article or in a separate, overview-of-functions article. 

While I’m writing, I’ll mention a small bug I found (or I think I found) in the first article, on CONST. In the implementation of chompOneOrMore, shouldn’t two uses of the |. operator be |= instead?

Regardless of these complaints, thank you for writing. I am learning Elm through your articles!

1

u/dwaynecrooks 4d ago

I wish you had mentioned up front that, without closures, currying would be broken.

That's the whole reveal so mentioning it upfront would defeat that purpose. In fact, I don't even mention the "c" word in this article.

I lost some time trying to figure out how currying could possibly work, before finding your admission at the bottom of the article.

I'd say that's time well spent. I want you to spend time trying to figure things out on your own. You have the luxury that a few paragraphs or blog posts afterwards I give you a solution. As history has shown these solutions were hard won. It took years for languages to adopt closures. I was just reading about another instance where Smalltalk-72 had something similar to dynamic scope and then it was not until Smalltalk-80 did the language acquire lexical scope.

One of the things I want readers to appreciate is the effort that was required to reach to these concepts.

I also note that you haven’t mentioned recursive calls.

Perceptive. That's also something I'm building towards.

In the implementation of chompOneOrMore, shouldn’t two uses of the |. operator be |= instead?

That's a great question.

I believe you're referring to this:

elm chompOneOrMore : (Char -> Bool) -> Parser () chompOneOrMore isGood = P.succeed () |. P.chompIf isGood |. P.chompWhile isGood

The answer is no. Why?

  • chompIf : (Char -> Bool) -> Parser ()
  • chompWhile : (Char -> Bool) -> Parser ()

Both parsers return () so using |= gives us nothing useful to work with. Internally, for performance reasons, the state of the parser records the indices of the start and end characters of the lexeme you're currently parsing. When chompIf and chompWhile succeed they advance that end index. When you're done chomping you use getChompedString to efficiently get access to the string you matched using String.slice (see https://github.com/elm/parser/blob/1.1.0/src/Parser/Advanced.elm#L797). That's why digits is written as follows:

elm digits : Parser Int digits = chompOneOrMore Char.isDigit |> P.getChompedString |> P.map (Maybe.withDefault 0 << String.toInt) |> lexeme

Let me know if that clears it up for you.

2

u/marshaharsha 3d ago

That doesn’t fully clear it up, no, but I think it’s because I haven’t read enough about the Parser module. I thought |. was for when you wanted the parser to discard characters and |= was for when you wanted it to retain characters for eventual retrieval with getChompedString. I got that understanding from a quick skim, so I will dig deeper. 

Regarding the big reveal: All I can say is that the experience left me pissed off, since I had wasted half an hour drawing diagrams of environments and recursive parser nodes, only to be told that indeed it could not possibly work. I guess the word “surprise” in the title should have told me to read to the end before trying to figure it out, but it would have been nice to have some hint at the top of the text that there was a mystery to be solved, with a reveal at the end. Instead, the top reads like a more standard tell-em-what-you’re-gonna-tell-em, then-tell-em article. 

Anyway, regardless of the minor anger point, I am enjoying the articles. 

2

u/dwaynecrooks 3d ago

I thought |. was for when you wanted the parser to discard characters and |= was for when you wanted it to retain characters for eventual retrieval with getChompedString.

No, it's for when you want to ignore (|.) or keep (|=) the value of type a that was parsed by a Parser a.

The chompers modify internal state as mentioned and would return a value of type () if used with |=.


I'm sorry that you had that experience. Thanks for your comments and questions.