Instead: "end" syntax, unified number type, all blocks produce new scopes (not just functions), local variables are explicit instead of default, etc.
The note about local variables is particularly important; we actively don't want Python's model of variables and scope.
The language itself looks pretty slick, and I am a sucker for gradual/optional typing. Do the type annotations result in performance/compiler optimization, as in Julia?
We've been designing the static system with exactly this in mind. The current implementation doesn't do anything fancy yet, but that's just because we've been getting off the ground and focusing on ergonomics and curriculum first. Getting performance in return for types is absolutely where we're going.
I've been burned by multiple return values numerous times in Scheme and Racket. They're very subtle and hard to make performant. It's one of those language features that very much does _not_ pay its own way, so you have to really, really need it to want to put it in your language. My view is that its uses are not many, and most (all?) can easily be achieved with literal objects. That's why Pyret doesn't have them and is unlikely to get them.
As far as I can tell the main advantage of multiple-return over literal objects is that callers who only care about the primary value can call the function the normal way, whereas if it's wrapped in an object they have to "pay" for it by explicitly extracting it. That's a nice-to-have but you seem to be saying that it isn't worth the trouble because the trouble is considerable. I'd like to hear more about why. (A friend is working on a language where this is an issue.)
p.s. This thread is just awesome. Thank you for engaging so extensively here.
2. Write me the identity function. (No, really. Stop. Try. Then read on.)
I hope you didn't say it's
fun id(x): x;
because if F returns multiple values and G consumes them, I can't refactor G(F(...)) into G(id(F(...))), which is pretty much the definition of an identity function. You should see the true identity function in R5+RS Scheme....
3. The way to make simple functions like that continue to work is to make the return values not be multiple values but a single one (i.e., some kind of tuple). At which point...
5. If you have multiple values, every single library function has to be rewritten to work with them. What should map do? filter? fold? And the hundred other library functions? It's also really hard to remember this when writing every line of library code (see #2).
6. We'd need a new binding construct in the language to bind the multiple return values. That's yet more syntax design, but also, it's yet more opportunity for typos to turn into indecipherable error messages ("Expected two values, got one" is a horrible thing to read for someone who doesn't even know a function can return two values!).
So, I claim this is simply not worth the frequency with which you actually need precisely this, as opposed to just returning some sort of tuple or object. Also, once I've bound the return value (say to r), saying r.x and r.y instead of just x and y is really not a big deal.
function id(...) return ... end
a, b = id(1, 2) --> a == 1, b == 2
c, d = id(3) --> c == 3, d == nil
e = id(4, 5, 6) --> e == 4, the rest is discarded.
The vararg system is built on a stack mechanism, and it is efficient.
Regarding map and other higher level functions, I'd only take the first return value.
If you have tuples it is indeed less of a concern. Better still if you have destructuring assignement, à la Julia.
Well, I did. :)
That's an interesting example. My first reaction was that one might be able to construct a similar anomaly by abusing optional arguments, but perhaps not: optional arguments to F don't get magically passed on to G. Still, I feel like I've seen function-signature constructs that would break the identity function similarly to how multiple-return does in your example. Maybe some twisted aspect of Common Lisp...
Still, there's an asymmetry, at least to our minds, between calls and returns, so the argument put forth for multiple values ("a function can take multiple arguments, why can't it return multiple answers?" -- made especially compelling in a CPS context, because returns turn into calls) doesn't quite play out in practice.
Ergo, no multiple return values.