I've watched Marcel's efforts with admiration for his commitment for a long time.
I looked at this and browsed some of the examples, and something clicked for me finally. I did a good solid 20 years of dedicated Smalltalk. When I started doing Objective-C, I was glad I could send messages, but noticed a number of differences. One of the things that you don't see just comparing syntax between Smalltalk and Objective-C is some of the deeper differences. Objective-S, like Objective-C, has type annotations. In ObjectiveC, there is a preference for void return types. Smalltalk, has no annotations, but all functions methods return. If you don't specifically return something from a method, self is the implicit return type. For any closure, it's always the last value of the expressions. It made cascading messages really easy, because methods tended to return the thing you wanted most of the time. When I started doing Objective-C, I commented on this lack of object return. I don't recall what the answers were, but it was clear that ship had sailed. :)
Since then, as I've sailed with Swift and Kotlin and Python, it's clear that that is the dominant pattern. Python, which doesn't have types, still returns None when nothing is specified. And then last year, I did some work on Erlang/Elixir. Like Smalltalk, all functions return something. You can return nil if you want, but you have to make the effort to do so. Otherwise, you just get the result of the last expression. And I loved it. It makes it so you can compose functional chains really easy and idiomatic.
It's led me to wonder why any language that wants to be more "functional" has a Void return type. And once again impressed that Smalltalk, for all of its OOness, is really quite functional in some ways. Functional programming, is of course, many things. But one of them is obviously the good old f(x) -> y thing we learn in high school. Functions take input, and return output. A function that returns nothing, isn't that useful. But we also have computerese "functions" that are more "subroutines". An imperative sequence of steps designed to produce a certain side effect. When one makes a Void function, it seems one is essentially saying "not interested in functional here, just want side effects." I'm not as opposed to side effect programming as some, but I guess I prefer the approach like Smalltalk and Elixir which strongly encourages returns of some value (even if ignored) so that one can be simultaneously functional and side-affecty.
I'm disappointed that Objective-S didn't embrace the no-such-thing-as-a-void-return philosophy of Smalltalk, even while lifting so much from Smalltalk.
Objective-S tries to be expression-oriented in its call/return parts, it even goes a bit further than Smalltalk in that the return of a method is the last expression of the method (like Erlang/Elixier, if I understood you correctly, and like Smalltalk blocks). For example here are some methods:
In fact, I don't even have a return statement. I am not sure I can keep that up, but so far it appears to be working out OK. I've appropriated the "^" to mean "send result to next filter" in the pipes/filter style:
This could probably be generalised so that it means "send result", which in a method means "to sender" and in a filter means "to next filter".
Coming back to void: if I want to have interop with Objective-C, I must also support void returns, and they do come in handy here and there. For example, stsh uses a method declaration in comments after the shebang to make scripts more method-like, including argument parsing and return values. The following script expects a single integer argument and prints the result of adding three to that number to stdout:
#!env st
#-addToThree:<int>arg
arg+3.
You will note that there is no printing code. The reason is that the "method declaration" says there will be a return value. That means stsh will take the value of the last expression of the script and print that. Very convenient (and "functional"/"expression-oriented"). It will also automatically parse the first command line arg to a number and assign to "arg". And if you don't give it an argument?
However, sometimes you don't want the script to be expression-oriented, you want the script to be in charge of printing to stdout. The following script, for example, is stream-oriented, it does not have a return value per-se, but rather streams its results:
In this streaming cases, having a return and being expression-oriented/functional does not make sense, and of course Objective-S tries to generalize beyond call/return.
Objective-C's default return type actually is "id" [1], and methods tended to return either some result or self up to NeXTstep 3.3, just like Smalltalk. So what changed? Distributed Objects. If your receiver is a distributed object, having a return value means that you have to marshal something, send it over the wire and synchronise on the return value. So they removed the default self returns.
With regards to types: I like type annotations, as long as I am not forced to add them everywhere and not constrained to the expressiveness of the type system. Having a bit of (checked) documentation is super-helpful when browsing code, having to chase down senders and implementors in order to figure out what a method does is super annoying. And of course Objective-C (and C) interop is crucial, as is the ability to compile some code to be as efficient as equivalent C code without needing a Heldencompiler/HeldenJIT. As soon as I add native compilation :-)
I looked at this and browsed some of the examples, and something clicked for me finally. I did a good solid 20 years of dedicated Smalltalk. When I started doing Objective-C, I was glad I could send messages, but noticed a number of differences. One of the things that you don't see just comparing syntax between Smalltalk and Objective-C is some of the deeper differences. Objective-S, like Objective-C, has type annotations. In ObjectiveC, there is a preference for void return types. Smalltalk, has no annotations, but all functions methods return. If you don't specifically return something from a method, self is the implicit return type. For any closure, it's always the last value of the expressions. It made cascading messages really easy, because methods tended to return the thing you wanted most of the time. When I started doing Objective-C, I commented on this lack of object return. I don't recall what the answers were, but it was clear that ship had sailed. :)
Since then, as I've sailed with Swift and Kotlin and Python, it's clear that that is the dominant pattern. Python, which doesn't have types, still returns None when nothing is specified. And then last year, I did some work on Erlang/Elixir. Like Smalltalk, all functions return something. You can return nil if you want, but you have to make the effort to do so. Otherwise, you just get the result of the last expression. And I loved it. It makes it so you can compose functional chains really easy and idiomatic.
It's led me to wonder why any language that wants to be more "functional" has a Void return type. And once again impressed that Smalltalk, for all of its OOness, is really quite functional in some ways. Functional programming, is of course, many things. But one of them is obviously the good old f(x) -> y thing we learn in high school. Functions take input, and return output. A function that returns nothing, isn't that useful. But we also have computerese "functions" that are more "subroutines". An imperative sequence of steps designed to produce a certain side effect. When one makes a Void function, it seems one is essentially saying "not interested in functional here, just want side effects." I'm not as opposed to side effect programming as some, but I guess I prefer the approach like Smalltalk and Elixir which strongly encourages returns of some value (even if ignored) so that one can be simultaneously functional and side-affecty.
I'm disappointed that Objective-S didn't embrace the no-such-thing-as-a-void-return philosophy of Smalltalk, even while lifting so much from Smalltalk.