Archive for the ‘what-if’ Category

A Taste of Nested Classes, intermission

Sunday, February 22nd, 2009

(Continues from part 3).

This post is an intermission because it’s not specifically about nested classes. In fact, it’s not about anything that really exists in Newspeak. Instead, it reviews a few hypothetical examples to show off some of the expressive capabilities of implicit receiver sends.

Most expressions in Newspeak begin with an implicit receiver send, at least conceptually. In something like

foo size

or

OrderedCollection new

both foo and OrderedCollection really are message sends, for some definition of “really”.

Of course, in some cases we can’t do without real receivers. There are literals, as well as traditional pseudo-variables (“reserved words”), whose interpretation should be hard-coded into the language.

Or should it be?

Starting with literals, imagine that every occurrence of a literal is compiled as an implicit receiver send identifying the type of the literal, with the “primitive” value of the literal as the argument. For example,

'Hello, brave new world!'

would really mean

(literalString: 'Hello, brave new world!')

with literalString: implemented in Object as

literalString: primitiveValue <String> = (
    ^primitiveValue
)

At this point this is only a possibility, but one that is explicitly mentioned in the language spec (5.1). If implemented, this would allow user code to redefine the interpretation of what appears to be literals for greater flexibility when implementing internal DSLs.

As for pseudo-variables, consider this implementation of the class Object:

class Object = (
    ...

    self = (
        "empty - return the receiver"
    )

    true = (
        ^True soleInstance
    )

    false = (
        ^False soleInstance
    )

    nil = (
        ^UndefinedObject soleInstance
    )

    thisContext = (
        ^ActivationMirror forSenderContext
    )
)

With this, most of the reserved words of Smalltalk become regular messages. The expression

^self foo: nil

now really means

^σ self foo: σ nil

with σ denoting a reference to (usually!) the receiver unrepresentable in the language syntax. We use the fact that an empty method effectively reads as

to grab that elusive σ in the self method.

super is more complicated because it doesn’t denote a separate object but rather self with a special mode of message lookup. However, even that can be lifted from language magic into a reflective implementation at the library level:

super = (
    ^SuperDispatcher forReceiver: self
)

with SuperDispatcher implementing doesNotUndestand: to reflectively look for the method implementing the message starting from the superclass of the implementor of the sender context’s method. (A mouthful, but that really is what it should do).

In a similar way, outer, the new reserved word of Newspeak not found in Smalltalk could be implemented as a message send handled by reflective code at the image level.

The last two examples, super and outer, are even more hypothetical than the other pseudo-variables. While technically possible, a reflective implementation of those is quite expensive compared to the usual “language magic” solution.

Also, this scheme in general leaves the meaning of things like self or nil open to changes by a class for all its subclasses and nested classes. Perhaps there is such a thing as too much flexibility. But even if these ideas are too much or too expensive for the core language, all of the same mechanisms are available to a designer of an internal DSL, and that’s what makes them interesting. For example, the “dispatcher receiver” pattern illustrated with super and outer was conceived in Hopscotch and used there to support NewtonScript-like object hierarchy-bound message dispatch.

(Continues in part 4).

Rethinking the comma

Sunday, September 2nd, 2007

This is a follow-up to the post about message chains from a few days ago. Like any syntax-related issue, the choice of the operator is one of those delightful things that encourage a lively exchange of opinions by virtue of being easy to have an opinion about. I ended the post half-seriously considering “:)”, but here is an interesting thought experiment.

Suppose we abolish a comma as a binary message. A + could just as well work as a generic “join these two things” message a comma usually is. Instead, suppose that the comma becomes the new chain operator. Here is what it would look like.

something thingsAt: aKey, includes: aThing,
    ifTrue: [...]
    ifFalse: [...]

This fits nicely with the original Smalltalk idea of using natural language punctuation for message control, and continues the line-up of a period and a semicolon by being the weakest message separator of them all.

Message chains

Thursday, August 30th, 2007

On the squeak-dev mailing list, Fabio Filasieno raised the issue of “pipes” in Smalltalk, which generated quite a discussion. Since I’ve provided an implementation, I’m also writing down my thoughts on the subject.

In a nutshell, this is what the idea is about. While the “classic” Smalltalk syntax allows us to write chains of unary messages such as

x foo bar

without explicit parentheses, this is not the case with keyword messages.

(x foo: 1) bar: 2

is a chain similar to the one above, yet its syntactic form is somewhat further removed from the idea of a “smooth” linear progression of operations, especially so when there are more than two consecutive message sends:

(((x foo: 1) bar: 2) baz: 3) zork: 4

Imagine now that we have an operator which I will for now designate as “:>” allowing us to rewrite the above as

x foo: 1 :> bar: 2 :> baz: 3 :> zork: 4

In other words, it makes the result of the preceding expression the receiver of the following message send, so that in fact

x foo :> bar :> baz

and

x foo bar baz

are two syntactic forms of the same chain of messages.

One obvious example of the use of this operator is rewriting something like

(aCollection select: [:some | some foo]) collect: [:each | each bar]

as

aCollection select: [:some | some foo] :> collect: [:each | each bar]

An even more likely case, which I don’t believe was mentioned on squeak-dev, are complex conditions:

something thingsAt: aKey :> includes: aThing :> 
    ifTrue: [...]
    ifFalse [...]

Writing the “standard” form often involves going back to the beginning of the expression to add parentheses once you’ve written enough of the expression to realize what it will look like. The chain operator exactly follows the “do this, then this, then that” structure of such expressions and does not require going back.

The change set linked at the beginning of the post implements the operator for Squeak. The inevitable question now is, Is This A Good Thing?

Clearly it adds nothing new to the language—chaining messages was always possible—only a new ability to express something in a different syntactic form. Such things generally invite (justified) suspicion and tossing around the various “less is more” quotes. Without questioning the value of minimalism and structured approach, some prior “sugary” additions such as tuples (brace Array constructors) or ifNil:/ifNotNil: could be (and have been) criticized on the same grounds. Any Array constructor discussion usually involves someone pointing out how the ease of array creation is overrated because redesigning the code to replace arrays with structured objects or store them in self-documenting variables only improves the code. This is true, of course—and yet in some contexts, one of which are DSL-like Smalltalk expressions, syntactically lightweight array creation turns out quite useful. Perhaps there is a similar “killer use” out there waiting for lightweight message chains to become available. Or perhaps not. My take is that this operator might be a good thing, and only practice can show if there are use cases out there waiting for it.

As for less is more—perhaps the power of Smalltalk is not as much in having a small untouchable core (and if anything, Object and UndefinedObject protocols in Squeak live to prove that more is more) as in having a core small and malleable enough to support this sort of extensions and experimentation. I’d rather say Squeak core is still not small and malleable enough if something like this can only be done by modifying the compiler rather than tweaking the meta-level.

Having said this, I should comment on Bert Freudenberg’s elegant #asPipe implementation. There are two reasons why I consider it very neat but still not enough of the real thing. One is the look of the code. The value proposition of pipes is improving readability. Getting rid of parentheses is only one step towards that, the other is having the links of the chain stand out enough for the reader to easily see where the distinct steps are. This is partly an issue of formatting, but proper “graphic design” of a visually distinct special operator is still unbeatable.

In fact, in terms of visibility I think the operator even better than “:>” (which already looks quite like a smiley) is “:)”:

something thingsAt: aKey :) includes: aThing :)
    ifTrue: [...]
    ifFalse [...]

The other issue are minor anomalies that are always hard to hide with DNU tricks because of the need for the DNU handler to have some rudimentary behavior of its own, and because some things are not handled by messages. For example:

2 asPipe between: 1 and: 3; == true

evaluates to false.

2 asPipe between: 1 and: 3; ifTrue: [#foo]

fails to compile, ruling out the use of pipes to simplify conditions.

foo := [#nil].
bar := [#notNil].
nil asPipe value; ifNil: foo ifNotNil: bar

does compile because the branches are not literal blocks, but then the result of the “pipe” is #notNil.