user59634
user59634

Reputation:

What is a 'thunk', as used in Scheme or in general?

I come across the word 'thunk' at a lot of places in code and documentation related to Scheme, and similar territories. I am guessing that it is a generic name for a procedure, which has a single formal argument. Is that correct? If yes, is there more to it? If no, please?

For eg. in SRFI 18, in the 'Procedures' section.

Upvotes: 74

Views: 21573

Answers (4)

ibrust
ibrust

Reputation: 177

In all cases the Thunk is injecting the result of some processing it does into another routine. It's essentially just a callback that returns a value. Yes, it is that simple and has been made that complicated.

Thunks first arose in the context of compiler optimization for languages with call-by-name function semantics. Most languages use call-by-value function semantics, where arguments are evaluated before they're passed to functions. But with call-by-name semantics the arguments aren't evaluated until they're used within the function. It's essentially a form of lazy evaluation.

Now, in a naive implementation, if you were to access these unevaluated, lazy arguments multiple times from within the function, the argument expression would end up being executed multiple times. Because you're actually just passing an expression to the function, not a value. As an optimization the compiler would go through and wrap these arguments with a Thunk. What a Thunk here does is it wraps the expression, and using memoization executes it / caches the result. The way memoization works is the parameters to a call are deduped... essentially when the parameters change call will be executed again, but if they don't change a cached result is returned instead. Hence in this context it's a compiler optimization.

Some functional languages provide facilities for generating Thunks. In this context, the Thunk is used to wrap a function being passed as a parameter. Here the Thunk is just an anonymous function with no parameters. This serves a specific purpose within the language to force the call-by-name semantic evaluation of the wrapped function, instead of call-by-value. The wrapped function is only evaluated when the Thunk is invoked. The Thunk returns the result of the wrapped function.

Redux uses what it calls Thunks, where it provides facilities to update some state after an async operation. Redux has a dispatcher that acts kind of like a runner, i.e. it will call your Thunks. The Thunk itself fetches some async result, or does some async calculation, and then calls the dispatcher a 2nd time, passing the result. So I suppose the idea is we're injecting the Thunks processing result into this dispatcher. It's a dubious use of the word "Thunk" though, because the "Thunk" isn't actually returning anything, really this is just a function passed to a runner, which happens to have a reference to the runner and calls it a second time. Anyway, Thunk is a very confusing term in this context, since it speaks to the internal implementation details of Redux, but from an outside programmers standpoint it's just a callback you're passing to a function, you never see the runner and shouldn't need to think about it. I would not refer to these as Thunks as it doesn't actually add anything and usually results in people being very confused.

So whether it's wrapping another function and returning its result, or doing memoization / caching, or in the case of Redux... telling the dispatcher to dispatch some result, it is nothing more than a callback which returns a result to be used within the function it's passed to.

Upvotes: 0

Johan Kotlinski
Johan Kotlinski

Reputation: 25759

Wikipedia has the following answer:

In functional programming, "thunk" is another name for a nullary function — a function that takes no arguments. Thunks are frequently used in strict languages as a means of simulating lazy evaluation; the thunk itself delays the computation of a function's argument, and the function forces the thunk to obtain the actual value. In this context, a thunk is often called a suspension or (in Scheme) a promise.

Adding a lazy evaluation example in Scheme. Here, promise is another word for thunk.

Upvotes: 19

Steven Huwig
Steven Huwig

Reputation: 20794

A "thunk" is a procedure object with no formal arguments, e.g. from your SRFI link:

(lambda () (write '(b1)))

The b1 variable is bound in the enclosing block, and this gives us a clue to the etymology of the word "thunk," which relies on a joke about poor grammar.

A zero-argument function has no way to change its behavior based on parameters it is called with, since it has no parameters. Therefore the entire operation of the function is set -- it is just waiting to be executed. No more "thought" is required on the part of the computer, all of the "thinking" has been done -- the action is completely "thunk" through.

That's all a "thunk" is in this SRFI's context -- a procedure with no arguments.

Upvotes: 47

Svante
Svante

Reputation: 51531

It is really simple. When you have some computation, like adding 3 to 5, in your program, then creating a thunk of it means not to calculate it directly, but instead create a function with zero arguments that will calculate it when the actual value is needed.

(let ((foo (+ 3 5))) ; the calculation is performed directly, foo is 8
  ;; some other things
  (display foo)) ; foo is evaluated to 8 and printed

(let ((foo (lambda () (+ 3 5)))) ; the calculation is delayed, foo is a
                                 ; function that will perform it when needed
  ;; some other things
  (display (foo))) ; foo is evaluated as a function, returns 8 which is printed

In the second case, foo would be called a thunk.

Lazy languages blur the line between binding a variable to a value and creating a function to return that value, so that writing something like the first form above is actually treated like the second, under the hood.

Upvotes: 90

Related Questions