what if no method syntax?
Now that method parameters and code block parameters are done the same way it does strike me as odd that we have two ways of naming a block of code. One as a method and one as closure.
thingy-closure = [ n: int -> r: int | r -> n + 1 ]
[ self thingy-method: n | n: int -> r: int | r -> n + 1 ]
We can give a block of code a signature which makes it all message-oriented. That's kind of the point of Smalltalk like syntax after all. The language already has a way to declare a method signature without the actual code though:
[ self thingy-method: n | n: int -> r: int ]
So what if we introduced a new piece of syntax. A method signature no longer defines the types. A method signature assigned to a code block creates a method. And a code block variable followed by a bracketed list does an evaluate:
thingy-closure =
[ self thingy-method: n ] =
[ self: my-thing, n: int -> r: int | r -> n + 1 ]
That's an extreme version where we are making a variable as well as a method declaration at the same time. A little odd but hey that's the fun of things sometimes. Now on to the new evaluate syntax:
thingy-closure(my-thing, 5)
my-thing thingy-method: 5
They're not quite equivalent. The original thingy-closure did not have a self parameter. But it does make things a little humorous in that we ended up with C like syntax as a bonus to method signatures.
The problem though is the signature syntax looks like a closure. We'd need to give it a new syntax. In Smalltalk there is no syntax for this. There is a way to reference the method name, in this case #thingy-method: using a hash tag before the name. Signatures are neater than that because they show you how to use the API.
For the sake of moving along let's naively assign the use of <> to method signatures. Declaring a method now looks like this:
<self thingy-method: n> [ self: my-thing, n: int -> r: int | r <- n + 1 ]
For fun I tried on ← instead of → to do the return/yield. I think I prefer ← to be honest. I also left off the assign because either you have a var = <signature> or you have <signature> [types | code] or the third less common variant: var = <signature> [types | code].
One of the weird flow-on effects of this is how then: is implemented:
<self then: true-block>
[ self: boolean, true-block: [ _: self -> ø ] -> return: true-block return-type |
self value == 1 then: [ true-block(self) ]
return <- ø ]
int then: int → ø is in the compiler so is not implemented here. Calling a block closure with (arguments...) is interesting. There's little difference here than using evaluate: but when you start interacting with other C (or C++) libraries there'd be a huge difference in quality of life.
I'm not quite sure what to think about this yet. Separating signatures in to their own bit of syntax doesn't seem to be a particularly big win. Doing evaluate using sneaky syntax seems at first like a win, but in reality it is done for closures not method and almost all interaction with C APIs is going to be calling methods, since C does not have the concept of a closure.
This experiment might be a bust. Time will tell if we come back to this or not.
Flipping the return arrow around is a winner though.