[Pharo-project] Explicit return in []
Hi this is early (at least to me) and I was asking myself a probably stupid question. But I would like to get your smart answers :) does a smalltalk like language really need explicit return in []? Q1 When are they absolutely required? Q2 What replacing mechanisms would be needed? Q3 What would we gain from an implementation stand point? It seems that in resilience lars removed them and more. Stef for Q1 I see foo x isZero ifTrue: [^ 33]. self continue
Q1 When are they absolutely required?
Not needed, but convenient. JavaScript for example does't have them. Things are done differently (exceptions, return values, ...).
Q2 What replacing mechanisms would be needed?
Exceptions are typically used to simulate non-local returns if really needed. Lukas
Q3 What would we gain from an implementation stand point? It seems that in resilience lars removed them and more.
Stef
for Q1 I see
foo x isZero ifTrue: [^ 33]. self continue
-- Lukas Renggli www.lukas-renggli.ch
On Sep 15, 2011, at 8:13 AM, Lukas Renggli wrote:
Q1 When are they absolutely required?
Not needed, but convenient. JavaScript for example does't have them. Things are done differently (exceptions, return values, ...).
do you have some examples how they do that?
Q2 What replacing mechanisms would be needed?
Exceptions are typically used to simulate non-local returns if really needed.
Lukas
Q3 What would we gain from an implementation stand point? It seems that in resilience lars removed them and more.
Stef
for Q1 I see
foo x isZero ifTrue: [^ 33]. self continue
-- Lukas Renggli www.lukas-renggli.ch
Q1 When are they absolutely required?
Not needed, but convenient. JavaScript for example does't have them. Things are done differently (exceptions, return values, ...).
do you have some examples how they do that?
For example, instead of #at:ifAbsent: the array/dictionary access in JavaScript returns a special "undefined" object if the object is not present. "undefined" is a distinct object, but it behaves similar to "null" (nil). Exceptions are rarely used in JavaScript. PrototypeJS uses (or used) them for example to break and continue in enumerables (http://www.prototypejs.org/api/enumerable/each). Lukas -- Lukas Renggli www.lukas-renggli.ch
Q1 When are they absolutely required?
Not needed, but convenient. JavaScript for example does't have them. Things are done differently (exceptions, return values, ...).
Q2 What replacing mechanisms would be needed?
Exceptions are typically used to simulate non-local returns if really needed. Lukas
Q3 What would we gain from an implementation stand point? It seems that in resilience lars removed them and more.
Stef
for Q1 I see
foo x isZero ifTrue: [^ 33]. self continue
-- Lukas Renggli www.lukas-renggli.ch
Avi posted an interesting reflection on Clamato's lack of explicit returns [1]: I *think* I like having left out explicit returns and implicitly
returning self, in favor of implcitly returning the value of the last statement. It unifies methods and blocks in a way I find pleasing. However, my hand was somewhat forced here by Javascript semantics. The only way to implement them (that I can think of) would be to throw an exception, which would get caught and rethrown at every level up the stack frame until it found the right one. I don't want to do this to the poor VM. I've provided a "self return: [:returnBlock | ...]" primitive that provides this when you really need it, but I've found it's rarely used.
[1] http://lists.squeakfoundation.org/pipermail/squeak-dev/2009-September/139196... On 15 September 2011 07:01, Stéphane Ducasse <stephane.ducasse@inria.fr>wrote:
Hi
this is early (at least to me) and I was asking myself a probably stupid question. But I would like to get your smart answers :)
does a smalltalk like language really need explicit return in []?
Q1 When are they absolutely required? Q2 What replacing mechanisms would be needed? Q3 What would we gain from an implementation stand point? It seems that in resilience lars removed them and more.
Stef
for Q1 I see
foo x isZero ifTrue: [^ 33]. self continue
Scheme does not have a return statement, so, no, ^ is not absolutely necessary. Removing it from the language, will probably significantly reduce the average method size. Alexandre On 15 Sep 2011, at 03:01, Stéphane Ducasse wrote:
Hi
this is early (at least to me) and I was asking myself a probably stupid question. But I would like to get your smart answers :)
does a smalltalk like language really need explicit return in []?
Q1 When are they absolutely required? Q2 What replacing mechanisms would be needed? Q3 What would we gain from an implementation stand point? It seems that in resilience lars removed them and more.
Stef
for Q1 I see
foo x isZero ifTrue: [^ 33]. self continue
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
On Thu, Sep 15, 2011 at 1:36 PM, Alexandre Bergel <alexandre.bergel@me.com> wrote:
Removing it from the language, will probably significantly reduce the average method size.
you mean "increase the average method size", right? -- Damien Cassou http://damiencassou.seasidehosting.st "Lambdas are relegated to relative obscurity until Java makes them popular by not having them." James Iry
No, decrease. Scheme forces you to have short functions, which usually begin with a condition or a case statement. Alexandre On 15 Sep 2011, at 08:49, Damien Cassou wrote:
On Thu, Sep 15, 2011 at 1:36 PM, Alexandre Bergel <alexandre.bergel@me.com> wrote:
Removing it from the language, will probably significantly reduce the average method size.
you mean "increase the average method size", right?
-- Damien Cassou http://damiencassou.seasidehosting.st
"Lambdas are relegated to relative obscurity until Java makes them popular by not having them." James Iry
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
On 15 September 2011 13:52, Alexandre Bergel <alexandre.bergel@me.com> wrote:
No, decrease. Scheme forces you to have short functions, which usually begin with a condition or a case statement.
It seems to me you assume that ^ is only used for local returns? How would you write the following method in Scheme (assuming that there is something like a #at:ifAbsent: in Scheme)? Object>>foo bar := zork at: 1 ifAbsent: [ ^ 2 ]. self andNowForSomethingCompletelyDifferent: bar. Lukas -- Lukas Renggli www.lukas-renggli.ch
How would you write the following method in Scheme (assuming that there is something like a #at:ifAbsent: in Scheme)?
Object>>foo bar := zork at: 1 ifAbsent: [ ^ 2 ]. self andNowForSomethingCompletelyDifferent: bar.
Either use a continution (call/cc), or check whether 1 is in zork, or check if looking for 1 return nil. But basically, it is likely that you will have one function for the the lookup, and another that does the andNowForSomethingCompletelyDifferent:. Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
How would you write the following method in Scheme (assuming that there is something like a #at:ifAbsent: in Scheme)?
  Object>>foo    bar := zork at: 1 ifAbsent: [ ^ 2 ].    self andNowForSomethingCompletelyDifferent: bar.
Either use a continution (call/cc), or check whether 1 is in zork, or check if looking for 1 return nil. But basically, it is likely that you will have one function for the the lookup, and another that does the andNowForSomethingCompletelyDifferent:.
You are changing the example, you were supposed to use #at:ifAbsent: :-) So this tells us what we all expected: If there is non-local return, people use other patterns. I doubt though that this generally leads to smaller methods or easier to understand code. Lukas -- Lukas Renggli www.lukas-renggli.ch
On 15 September 2011 14:20, Lukas Renggli <renggli@gmail.com> wrote:
How would you write the following method in Scheme (assuming that there is something like a #at:ifAbsent: in Scheme)?
  Object>>foo    bar := zork at: 1 ifAbsent: [ ^ 2 ].    self andNowForSomethingCompletelyDifferent: bar.
Either use a continution (call/cc), or check whether 1 is in zork, or check if looking for 1 return nil. But basically, it is likely that you will have one function for the the lookup, and another that does the andNowForSomethingCompletelyDifferent:.
You are changing the example, you were supposed to use #at:ifAbsent: :-)
So this tells us what we all expected: If there is non-local return, people use other patterns. I doubt though that this generally leads to smaller methods or easier to understand code.
s/non-local/no non-local/ Lukas -- Lukas Renggli www.lukas-renggli.ch
So this tells us what we all expected: If there is non-local return, people use other patterns.
Exactly.
I doubt though that this generally leads to smaller methods or easier to understand code.
Maybe I talked here for my personal experience. I asked some schemers around me, and they think there is no relation between the existence of return and the function length. Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
On Wed, Sep 14, 2011 at 11:01 PM, Stéphane Ducasse < stephane.ducasse@inria.fr> wrote:
Hi
this is early (at least to me) and I was asking myself a probably stupid question. But I would like to get your smart answers :)
does a smalltalk like language really need explicit return in []?
As Lukas says, not if one has exceptions. But Smalltalk-80 V2 didn't have exceptions, and so needed ^-return. Personally I find ^-return elegant and a vital part of the language's simplicity. If ^-return is not allowed in blocks then blocks are special-cases, must be explained, and avoided in certain cases, etc. Getting rid of it pushes the language in a more traditional convenient-for-the-language-implementor direction that I think is a serious mistake. Language should serve the author/reader, *not* the implementor.
Q1 When are they absolutely required? Q2 What replacing mechanisms would be needed? Q3 What would we gain from an implementation stand point?
Some simplicity, certainly. But (IMO) ^-return isn't as complex as method lookup and message cacheing so you're not reducing complexity overall.
It seems that in resilience lars removed them and more.
Yes, IIRC he got rid of first-class blocks. They can only be downward funargs (passed as parameters), not upward funargs (stored in inst vars, returned as results and hence used after their enclosing activation has returned).
Stef
for Q1 I see
foo x isZero ifTrue: [^ 33]. self continue
-- best, Eliot
As Lukas says, not if one has exceptions. But Smalltalk-80 V2 didn't have exceptions, and so needed ^-return.
Yes this is really what I was thinking too.
Personally I find ^-return elegant and a vital part of the language's simplicity.
I agree but I wanted to really understand it because lars is not a fool.
If ^-return is not allowed in blocks then blocks are special-cases, must be explained, and avoided in certain cases, etc. Getting rid of it pushes the language in a more traditional convenient-for-the-language-implementor direction that I think is a serious mistake. Language should serve the author/reader, *not* the implementor.
In my question came when I realized stupidly that "home" or the stuff to jump (to a context) was a context and not a compiled method. Because we need to jump to an activation and not just a bytecode holder. So I tried to understand the jumping behavior implication. In fact my real question was would not having ^-return in [] change the closure implementation and in particular implementation of home. Would home be required to return a context and if needed. I guess not. I should look at home senders. When I coded a simple (probably too simple) scheme/lisp interpreter, closures were trivial (got an alist chained to nested environment) and it was it. Now I was trying to understand (this is why I'm looking at the block implementation) is the next question: if removing ^- was leading to a if removing ^- was like removing upward funargs. Now I implemented call/cc in a probably bad ways (CSP) so fast versions probably introduces context too. since this is just copying the activation stack and jumping to it.
Q1 When are they absolutely required? Q2 What replacing mechanisms would be needed? Q3 What would we gain from an implementation stand point?
Some simplicity, certainly. But (IMO) ^-return isn't as complex as method lookup and message cacheing so you're not reducing complexity overall.
It seems that in resilience lars removed them and more.
Yes, IIRC he got rid of first-class blocks. They can only be downward funargs (passed as parameters), not upward funargs (stored in inst vars, returned as results and hence used after their enclosing activation has returned).
Yes exactly. So I wanted to know if removing ^- was like removing upward funargs.
Stef
for Q1 I see
foo x isZero ifTrue: [^ 33]. self continue
-- best, Eliot
On 15 September 2011 20:38, Stéphane Ducasse <stephane.ducasse@inria.fr> wrote:
As Lukas says, not if one has exceptions. Â But Smalltalk-80 V2 didn't have exceptions, and so needed ^-return.
Yes this is really what I was thinking too.
Personally I find ^-return elegant and a vital part of the language's simplicity.
I agree but I wanted to really understand it because lars is not a fool.
 If ^-return is not allowed in blocks then blocks are special-cases, must be explained, and avoided in certain cases, etc.  Getting rid of it pushes the language in a more traditional convenient-for-the-language-implementor direction that I think is a serious mistake.  Language should serve the author/reader, *not* the implementor.
In my question came when I realized stupidly that "home" or the stuff to jump (to a context) was a context and not a compiled method. Because we need to jump to an activation and not just a bytecode holder. So I tried to understand the jumping behavior implication.
In fact my real question was would not having ^-return in [] change the closure implementation and in particular implementation of home. Would home be required to return a context and if needed. I guess not. I should look at home senders.
When I coded a simple (probably too simple) scheme/lisp interpreter, closures were trivial (got an alist chained to nested environment) and it was it. Now I was trying to understand (this is why I'm looking at the block implementation) is the next question: if removing ^- was leading to a  if removing ^- was like removing upward funargs.
Now I implemented call/cc in a probably bad ways (CSP) so fast versions probably introduces context too. since this is just copying the activation stack and jumping to it.
Do you mean CPS/continuation passing style? I've been thinking about how to do CPS on Smalltalk, these past few weeks, precisely for (delimited) continuations. I'd like to contrast the technique with my current implementation, using exceptions. How are you finding it? Delimited continuations are superior to call/cc at any rate: it's trivial to express call/cc in terms of delimited continuations (wrap your entire process in a prompt and you're done), but the converse isn't true. You can fake delimited continuations a la call/cc + mutable cell, but it's messier. Also, undelimited continuations can't be treated like functions, and can't compose. Oleg Kiselyov's written a fair about the issue, and continuations in general: * http://lambda-the-ultimate.org/node/4313 * http://okmij.org/ftp/continuations/undelimited.html * http://lambda-the-ultimate.org/node/86 * http://okmij.org/ftp/continuations/index.html frank
Q1 When are they absolutely required? Q2 What replacing mechanisms would be needed? Q3 What would we gain from an implementation stand point?
Some simplicity, certainly. Â But (IMO) ^-return isn't as complex as method lookup and message cacheing so you're not reducing complexity overall.
It seems that in resilience lars removed them and more.
Yes, IIRC he got rid of first-class blocks. Â They can only be downward funargs (passed as parameters), not upward funargs (stored in inst vars, returned as results and hence used after their enclosing activation has returned).
Yes exactly. So I wanted to know if removing ^- was like removing upward funargs.
Stef
for Q1 I see
foo     x isZero ifTrue: [^ 33].     self continue
-- best, Eliot
Now I implemented call/cc in a probably bad ways (CSP) so fast versions probably introduces context too. since this is just copying the activation stack and jumping to it.
Do you mean CPS/continuation passing style?
yes but this was a stupid and simple implementation of a simple scheme. Just plain boring to debug but nothing fancy.
I've been thinking about how to do CPS on Smalltalk, these past few weeks, precisely for (delimited) continuations.
I think that if this is not done automatically by program transforamtion you end up creating ugly code.
I'd like to contrast the technique with my current implementation, using exceptions. How are you finding it?
Delimited continuations are superior to call/cc at any rate: it's trivial to express call/cc in terms of delimited continuations (wrap your entire process in a prompt and you're done), but the converse isn't true. You can fake delimited continuations a la call/cc + mutable cell, but it's messier. Also, undelimited continuations can't be treated like functions, and can't compose. Oleg Kiselyov's written a fair about the issue, and continuations in general:
* http://lambda-the-ultimate.org/node/4313 * http://okmij.org/ftp/continuations/undelimited.html * http://lambda-the-ultimate.org/node/86 * http://okmij.org/ftp/continuations/index.html
I'm sorry I did that nearly 10 years ago just for fun and a lecture so I forgot :) Now I do not know delimited continuations Stef
frank
Q1 When are they absolutely required? Q2 What replacing mechanisms would be needed? Q3 What would we gain from an implementation stand point?
Some simplicity, certainly. But (IMO) ^-return isn't as complex as method lookup and message cacheing so you're not reducing complexity overall.
It seems that in resilience lars removed them and more.
Yes, IIRC he got rid of first-class blocks. They can only be downward funargs (passed as parameters), not upward funargs (stored in inst vars, returned as results and hence used after their enclosing activation has returned).
Yes exactly. So I wanted to know if removing ^- was like removing upward funargs.
Stef
for Q1 I see
foo x isZero ifTrue: [^ 33]. self continue
-- best, Eliot
On Thu, Sep 15, 2011 at 12:38 PM, Stéphane Ducasse < stephane.ducasse@inria.fr> wrote:
As Lukas says, not if one has exceptions. But Smalltalk-80 V2 didn't have exceptions, and so needed ^-return.
Yes this is really what I was thinking too.
Personally I find ^-return elegant and a vital part of the language's simplicity.
I agree but I wanted to really understand it because lars is not a fool.
If ^-return is not allowed in blocks then blocks are special-cases, must be explained, and avoided in certain cases, etc. Getting rid of it pushes the language in a more traditional convenient-for-the-language-implementor direction that I think is a serious mistake. Language should serve the author/reader, *not* the implementor.
In my question came when I realized stupidly that "home" or the stuff to jump (to a context) was a context and not a compiled method. Because we need to jump to an activation and not just a bytecode holder. So I tried to understand the jumping behavior implication.
In fact my real question was would not having ^-return in [] change the closure implementation and in particular implementation of home. Would home be required to return a context and if needed. I guess not. I should look at home senders.
Indeed. Not having ^-return means closures wouldn't need a home. In VisualWorks this is at the heart of the full/non-full block distinction. Full blocks have a home context, non-full (copying & clean) blocks don't have a home. So instantiating copying blocks is quicker since it doesn't involve instantiating the block's enclosing context. You'll see in my closure implementation that I took a short-cut and just ate the cost of instantiating the enclosing context (outerContext) to avoid having a) to implement the analysis in the compiler and b) to keep the number of bytecodes down). In a later image format/bytecode format I could revisit that decision. In particular, BlockClosure needs separate method and receiver inst vars so that it can be activated independently. Right now the value[:value:*] primitives fetch method and receiver from outerContext. But look, once the new object representation/GC is finished everything will be significantly faster anyway. So I wouldn't worry too much about this. Right now things are simple; we have only one kind of block, and performance in some cases is slightly sub-optimal. That's an acceptable price for now, no? When I coded a simple (probably too simple) scheme/lisp interpreter,
closures were trivial (got an alist chained to nested environment) and it was it. Now I was trying to understand (this is why I'm looking at the block implementation) is the next question: if removing ^- was leading to a if removing ^- was like removing upward funargs.
Now I implemented call/cc in a probably bad ways (CSP) so fast versions probably introduces context too. since this is just copying the activation stack and jumping to it.
Q1 When are they absolutely required? Q2 What replacing mechanisms would be needed? Q3 What would we gain from an implementation stand point?
Some simplicity, certainly. But (IMO) ^-return isn't as complex as method lookup and message cacheing so you're not reducing complexity overall.
It seems that in resilience lars removed them and more.
Yes, IIRC he got rid of first-class blocks. They can only be downward funargs (passed as parameters), not upward funargs (stored in inst vars, returned as results and hence used after their enclosing activation has returned).
Yes exactly. So I wanted to know if removing ^- was like removing upward funargs.
Well, one can't remove upward fiunarg support without removing ^-return. But one can implement downward funargs in a number of ways, so this isn't the only issue.
Stef
for Q1 I see
foo x isZero ifTrue: [^ 33]. self continue
-- best, Eliot
-- best, Eliot
participants (7)
-
Alexandre Bergel -
Damien Cassou -
Eliot Miranda -
Frank Shearar -
Lukas Renggli -
Nick Ager -
Stéphane Ducasse