Currying in Smalltalk

Currying, unless you are into Thai cooking, is partial function application commonly used in functional languages. (Some argue it should be called Schönfinkelisation, but oddly so far the term hasn’t caught on). Given a function of N arguments, one can invoke it with K arguments, K < N. This produces a new “curried” function of N – K arguments, with the K arguments of the original call closed over (remembered).

In Smalltalk, currying would allow the example:

| add inc |
add := [:a :b  | a + b].
inc := add value: 1.
(inc value: 2) + (inc value: 3)

run without errors and produce 7. This feels like a significant change down in the foundations of the universe, but amazingly it is trivial to implement.

Both in VisualWorks and Squeak, when a Block is invoked with a wrong number of arguments, message processing ends with a primitive failure in the valueWithArguments: method. The standard Smalltalk response to that is to complain about the wrong number of arguments. All we need is change that response to curry the receiver when appropriate.

To make the example shorter and nicer (we’ll see why a little later), we define a class called CurriedBlock, with instance variables block and arguments and an obvious initialization method block:arguments:. We also change the standard valueWithArguments: as follows:

valueWithArguments: anArray
	<primitive: 500>
	(anArray isMemberOf: Array)
		ifTrue: [anArray size < self numArgs
			ifTrue: [^CurriedBlock new block: self arguments: anArray]
			ifFalse: [self error: 'Incorrect number of arguments']]
		ifFalse: [self error: 'valueWithArguments: requires an Array']

The method above is a simplification of the VisualWorks version, with the horrible UserMessages replaced by readable strings. The Squeak version is nearly identical.

CurriedBlock needs to reproduce the standard block protocol:

valueWithArguments: anArray
	^block valueWithArguments: arguments, anArray

value: anObject
	^block valueWithArguments: (arguments copyWith: anObject)

... other required value:... methods

numArgs
	^block numArgs - arguments size

This is all that is needed to make the example work.

In fact, we could do without the CurriedBlock class. Our implementation of valueWithArguments: could answer a real block by doing something like:

valueWithArguments: anArray
	...
		ifTrue: [anArray size < self numArgs
			ifTrue: [^self curriedWithArguments: anArray]
	...

curriedWithArguments: anArray
	| arity |
	arity := self numArgs - anArray size.
	arity = 1 ifTrue:
		[^[:arg | self valueWithArguments: (anArray copyWith: arg)]].
	arity = 2 ifTrue:
	...

However, since Smalltalk blocks don’t support the equivalent of Lisp “rest” arguments, the curriedWithArguments: method would have to explicitly enumerate and handle all arities we realistically expect to use in block invocations. Using CurriedBlock instead produces a nicer example.

To be continued.

8 Responses to “Currying in Smalltalk”

  1. Sean says:

    Thanks for the explanation! I just heard this term during a podcast on dynamic languages by some guys that were Smalltalkers (Ruby was their game). They struggled to explain this concept. As you can imagine it’s difficult to do without code examples. Now, thanks to you, I understand it.

    Sean

  2. Terry says:

    Vassili

    I believe your code snippet is incorrect, as it will not evaluate.
    I think you meant to write;

    | add inc |
    add := [:a | [:b | a + b]].
    inc := add value: 1.
    (inc value: 2) + (inc value: 3)

  3. Terry says:

    argh!

    That is what I get for not reading the whole article before commenting.

  4. Anthony says:

    Clever implementation, Vassili :)

  5. David Morley says:

    Vassili? Is it you? Moscow, 1993?

    Still doing Smalltalk? You’re lucky. The rest of us were forced out of Smalltalk in ~1997. Vadim quit, in part because of this. Andre held on as long as possible. Most of the others never did get it. The newer hires have never heard of it.

    I’d love to hear from you. Pls contact me at [address removed to prevent harvesting by spambots --v].

    [editor: please forward this to Vassili Bykov]

  6. Steve Taylor says:

    Previous comment by ‘Walter Arlene’ is spam (mouse over their name and see their url is a link to a classified ad site and their post is content free.)

    Also – excellent post on currying. I’m always blown away how Smalltalk (and some other dynamic languages, to be fair) can be made to do things they were never designed to do with minimal effort.

  7. [...] posts del blog de Vassili Bykov sobre curried blocks: Currying in Smalltalk, donde muestra cómo implementar bloques currificados, la parte dos, donde currifica los bloques [...]

  8. chaetal says:

    It’s not currying, it’s partial application. No?

Leave a Reply

For spam filtering purposes, please copy the number 8600 to the field below: