Thunk vs. lambda and doc confusion

Hi team,

The Thunk glossary entry has a second example that is supposedly turning a function into a thunk, yet it uses the lambda keyword!

But thunks exist as a keyword, so why use lambda?

Am I missing something, or is that confusing?

I pasted this exact question to ChatGPT, and got a pretty good answer, so I will just paste the response here (with slight modifications to improve correctness).

the confusing part is that “thunk” and thunk are two different things.

The glossary is using thunk in the general programming-language sense: a thunk is a procedure that takes zero arguments. A thunk can be constructed in many different ways. The example uses lambda to construct a thunk. But thunk can also be used to construct a thunk.

Meanwhile, thunk is a Racket macro/syntax. It is essentially a convenience for constructing a thunk. (thunk (displayln "yes")) is just another way of writing something equivalent in purpose to (lambda () (displayln "yes")).

The important distinction is:

  • thunk — a concept: a zero-argument procedure whose body can be run later.
  • thunk — a particular Racket macro provided by the language/library to construct a thunk, but not necessarily the only way to construct a thunk.
  • lambda — a core form for constructing procedures, including zero-argument procedures (thunk).

Really disappointing to come here for conversation with people and getting an "answer" pasted from an LLM that I also have access to :expressionless: Thanks for editing it, at least.

Alright, now you have a full human response.

Let me ask you (and @LiberalArtist and @Zeb who seem to share your concern) this: how do you want the response to be different?

On top of the raw LLM response, I corrected how it says that thunk is a procedure. I cut about 5 verbose paragraphs that I thought have little values (e.g., it spent a few paragraphs affirming that the glossary is poorly worded, which I disagree with). I added the bit that a thunk can be constructed from various ways to make the answer flows better. And I vetted that everything is accurate. Most text is still from the original response though.

Would having me spending time typing the answer out character-by-character be really that valuable to you?

Now two topics. I'll nip the first:

  1. The question is when/whether we should be asking each other questions and having discussions when LLMs can "do it all anyway". That's societal, and locally cultural (eg. the Racket community), and I cannot answer it, only indicate my personal disappointment when I ask for a yarn and get modem noises. Eeeeeee ahhhhh brrrrrrrrrrrrr. Beep beep boop.

On topic:

  1. I believe you have answered my question, thanks again for the effort. My rewording to check comprehension: Yes, it's a little weird that a glossary entry doesn't use the keyword of the same name, but that is for conceptual clarity and explanatory power. The way you make a little unit of deferred execution (ie. a "thunk") is with a lambda, and the thunk keyword is just syntatic sugar for that same thing.

(By the way, I'm trying to not type anything out character-by-character these days because speech recognition has gotten so good! Currently using the very lightweight asryx and can't believe it actually works given its size and simplicity).

But thunks exist as a keyword, so why use lambda?

  1. To nitpick, thunk is not a "keyword" -- it's not special or privileged. It's merely a macro, which anyone could write themselves -- not just the creators of Racket. I nitpick because this extreme extensibility a pretty important idea in Racket (and some other languages including most lisps).

    There are a bunch of macros and functions like thunk defined in the racket/function module. Speaking of which...

  2. Another small point to consider is that racket/base is a lighter set of things to require in a program -- unlike racket, which automatically imports dozens of libraries like racket/function. Often people avoid racket because it's so "heavy", use racket/base, and require just the specific modules they need.

    In a case like that, I might decide to just write out a thunk using lambda instead of require-ing racket/function just to get thunk. (Also I might just write (λ () _) which is shortest of all. :wink:)

    So that is one literal answer to "why use lambda?" even though there exists a convenience macro like thunk (which again is not a keyword, and you could write yourself! :smile:).

I am probably the least-big fan of LLMs here, so I understand where you're coming from.

OTOH @sorawee did disclose, and did proof-read and edit. You got not just an "answer" but an answer, IMHO.

Also, if you want to be mad, I would prioritize first being mad about the "Summarize with AI" button that Discourse slammed into the UI here. That is present on every flopping topic here.

The one thing I'd add is that is that I almost never use the thunk syntactic sugar, and I also don't see it especially often in code I read. Some people like it and use it often, and there's nothing wrong with it, I just wouldn't want to give the impression that it's “better style” or something to write a thunk using thunk. In particular, note that (λ () 1) is actually shorter than (thunk 1).

(On the other hand, thunk is used idiomatically with for/foldr, where it's helpful to have a keyword of the same shape as other delay-like forms.)


First let me say that I don't claim to have a definitive answer to this area of rappidly-evolving social norms!

Given the premise of starting with an LLM response, I think your post handled it more or less optimally. Even before you described your modifications in more detail, I assumed you had done something like that, first because the content was all accurate and relevant (which I can rarely say of raw LLM output), but also because you have been a trusted member of this community for a long time.

One thing I wonder is, given the amount of editing and checking for accuracy, does starting with an LLM still feel like it saves you time, effort, etc.? (Even ignoring this ensuing meta-discussion.)

Part of my concern is for the usefulness of answers for people newer to the Racket community. Since I already knew how I would answer this question, I could check the LLM output for accuracy, and I also had confidence that you, @sorawee, would have done so. But if I didn't already have a level of trust in you, and I couldn't check the accuracy of the LLM output myself—because I didn't already know there answer to my question—I would have very low confidence in relying on it.

I can also imagine it feeling offputting from the perspective that, if I'd wanted an LLM’s response, I could have asked one.

Another concern is that, often, I find the conversation in these threads at least as valuable as an “answer” per se. That's especially true with a question like this one, which is largely about matters of style and taste, where responding to questions is one of the main ways perspectives get written down. For instance, I know there are some Racketeers who do use thunk regularly, and I'd be interested to hear their perspectives! To me, LLM output doesn't add to these conversations, and editing the output seems only partially to help: what I really want to know is each commenter’s personal thoughts.

I am glad that the Racket community includes people with a broader range of stances toward LLMs than my personal views, and I'm not advocating for any sort of “rule.” But there are some of my concerns about LLM-driven responses, especially if they were to become more widespread while being handled with less care than you did, @sorawee.

To further nitpick, sometimes “keyword” is used to mean the identifier which introduces a macro or other syntactic form. I think that use is more common in broader Scheme circles, but I believe I've seen it in Racket contexts, too. I tend to avoid using it that way, in part because we usually use “keyword” to mean #:one-of-these (which I think is why it's a less common formulation in Racket in general). But that is the sense that I understood here.

Since you asked :wink:

The advantage of thunk for me when I use it is probably semantics: it names the thing. (It’s also easier to remember “thread over thunk” in my head.) And thunk* is nice for not naming an unused arg list.

And I get to type one less set of (), but that’s a finger macro at this point, so…

Somehow I understood OP to mean "keyword" in the sense of "reserved keyword" in many languages.

But I might have misunderstood.

Anyway that's a great point that, in Racket, "keyword" usually means #:one-of-these.

The history of the term always makes me smile. Algol 60 had call by name, which meant expressions as a parameter of a routine could be evaluated multiple times. The fix was to wrap it in a tiny little subroutine that returns a reference to the result value. When looking at, they realized they could do some compile time optimizations because the type was already "thought of" by the compiler. Or, as they put it, the compiler had thunked it.

Hence the macro. Also, while this isn't true in Racket, the original intent is that the function will always return the same value, so it can be evaluated once and later uses of the thunk are replaced with a simple memory dereference. Some use the macro to imply those semantics, where as a lambda () ... is just like any other function.

I don't think I've heard that explanation of the name before. I heard it explained as "the sound of an empty activation record hitting the stack".

For the record, this conflates two distinct concepts: thunks and delays.

The point of a (n Algol) thunk is to return the result of re-evaluating an argument whenever it is referenced. Algol's designers understood that this could one a different value for each reference. There is even a name for exploiting this property: Jensen's device.

The point of a 'delay is to cache the result from the first evaluation, even if this is an exception in Racket, and to yield this result every time it is requested again. The idea goes back to the "Lazy Cons" article of Friedman and Wise. Haskell is the modern version of this.

-- Matthias, an Algol programmer