stz receiverless

stz receiverless
Photo by Anne Nygård / Unsplash

Do we always need a receiver? the idea behind 'message sends' was that an object is sending a message to another object. It's a fun idea. But sometimes it's awkward to choose one parameter over another, eg:
start to: stop do: block

This looks fine when we have a range but when we don't we're trying to be literate about our method names. This is a Smalltalk legacy. A fun one. Programming should have a bit of fun to it. But if we're being honest about literacy, the following is more literate:
from: start to: stop do: block

If the method is from:to:do: just what is the receiver? It could be the module you've imported... and may be it should be:
core from: start to: stop do: block

That looks... wrong. Core has nothing to do with our literacy here. But if we've not imported the methods in to our compilation scope context then that's what it would look like. Just what are we forced to import to get these core concepts. What do we flood our own namespace with?

The classes are accessible as module.class if you do a using:, but accessible as class if you do an import:. Receiverless methods still need to be namespaced. Consider an external interface to, say, opengl. You want to be able to write the following:
opengl.attach-shader: program, shader

If you import then you're taking on the burden of having attach-shader as a locally defined method. What this tells us, then, is that core.from: start to: stop do: block is the correct syntax smells here is the method name. This particular receiverless method is not helpful. Ranges are helpful:
(1 to: 100) do: [ i | ... ]

Let's make from:to:do: an internal facility:

range := { start: $T, stop: $T }

[ range do: block | range-of: $T, ($T) -> $R |
  from: range start to: range stop do: block ]

[ start to: stop do: block | $T, $T, ($T) -> $R |
  block evaluate: start
  start == stop else: [ start + 1 to: stop do: block ]
]

There's one more thing I'd like to change here. Well, not so much change but as to highlight. I've made a random choice that the first parameter becomes contextualised in your execution scope. It makes it easy to send messages to 'self' without writing self constantly. But it also means the variables are accessible to you. So let's try that again. A small change and a guard clause and suddenly we have our first piece of library code:

using: 'core'

range = { start: $T, stop: $T }

export:
[ range do: block | range of: $T, ($T) -> self |
  core.assert: start < stop
  from: start to: stop do: block ]

[ start to: stop do: block | $T, $T, ($T) -> $R |
  block evaluate: start
  start == stop else: [ start + 1 to: stop do: block ] ]

May be this is even correct code for stz. I'm honestly not sure. But it seems receiverless disappeared the more we poked at the problem. What about the opengl.attach-shader: call though? We did something similar in the method above - core.assert: bool. Receiverless does have its place. External libraries for sure but also 'utility' things that don't need a receiver.

Another example might be stdio.stdout print: 'Hello World'. Or even log.info: 'This is information'. Some experimentation would be required. One thing is for sure, this is still all objects all the way down - it just doesn't look quite like it all the time. I'm happy with this trade off.