Smalltalk

multiple returns (maybe with decent formatting this time)

December 2, 2003 22:37:21.172

Warning: this contains code fragments with the pre tag, which look OK in a web browser but not in BottomFeeder

A general bit of Smalltalk stuff, prompted by a discussion from comp.lang.smalltalk. Usually I avoid threads with 50 messages a day, but I sampled one randomly and it reminded of a favourite topic. Credit to Anthony Lander for this insight. For the original, and some interesting material on doing 3-d graphics and video games with Smalltalk, see this link (PDF) . This part is right at the end, page 56-58.

Multiple returns is a language feature that lets a method return more than one value. It allows you to avoid having a method return a collection when all you really want is to return two values and get them into variables. Python is probably the most popular current language with this feature.

So, in Smalltalk, suppose that we had two collections and we want to know the elements in one but not the other, delete the old ones that are gone, and insert the new ones. We could do this as

	  beforeButNotAfter:= self findElementsOf: before notIn: after.
	  afterButNotBefore := self findElementsOf: after notIn: before.
	  beforeButNotAfter do: [:each | each delete].
	  afterButNotBefore do: [:each | each insert].

But apart from being verbose that's probably not too efficient. It'd be nice if we could do it in one statement.

	beforeButNotAfter, afterButNotBefore := self findNonOverlappingElementsOf: before and: after.
	beforeButNotAfter do: [:each | each delete].
	afterButNotBefore do: [:each | each insert].

Where the comma on the left of an assignment indicated that the method returned two values, and the first one went into the first variable, the second into the second variable. But in Smalltalk about the best we can do is to make a temporary collection

	result := self findNonOverlappingElementsOf: before and: after.
	beforeButNotAfter := result first.
	afterButNotBefore := result last.
	beforeButNotAfter do: [:each | each delete].
	afterButNotBefore do: [:each | each insert].

Which isn't nearly as nice. The insight, however, is that blocks give us a lot of the same kind of power. Consider

	self 
		findNonOverlappingElementsOf: before 
		and: after 
		doing: [:beforeButNotAfter :afterButNotBefore |
			beforeButNotAfter do: [:each | each delete].
			afterButNotBefore do: [:each | each insert]].

Here, instead of multiple return into temporaries, we have a block that defines block temporaries. Our values get into variables with nice names. In the original example that Anthony showed there was an additional wrinkle. This was in the context of 3-d graphics, computing intersections of collections of planes. But they might not intersect. By using the block we can encapsulate the test that's required for that condition, which we otherwise have to put into our code as e.g. a nil test.

For me, this was a real eye-opener. I've used blocks an awful lot, for an awfully long time, even using this kind of a pattern, but it had never occurred to me as being an alternative to multiple return.

Comments

Untitled

[Vassili Bykov] December 3, 2003 0:54:51.334

Or a bit closer to home, see Win32DialogSupplier>>parseDefault:into:ifGarbage:. Unfortunately, its sender has been mutilated by the RB formatter and is messier than it once was.

Untitled

[Vassili Bykov] December 3, 2003 1:04:29.715

And also, those crippled by thinking too much about language semantics, will note that since a function return is equivalent to calling a continuation at the function call site, a continuation for a call site with a multiple-value return is nothing other than a multiple-argument lambda function. In other words, this trick *is* multiple-value return in CPS, and not just a clever hack to fake one.