I'll just throw this out and see what turns up. As you have probably heard, I am toying with adding eventual refs to Squeak. Unfortunately, they don't play well with non-local returns. Igor and I had been discussing what could be done with methods having non-local returns adn it is looking nasty. So I thought to look at another piece of the puzzle and question its existence.
How important is non-local return to Squeak? What would Squeak look like without it?
So I thought of the first use of it, detecting an object in a collection. Here is #detect:ifNone: with non-local return:
detect: aBlock ifNone: exceptionBlock self do: [:each | (aBlock value: each) ifTrue: [^ each]]. ^ exceptionBlock value
and here is a version without non-local return:
detectNoNonLocalReturn: aBlock ifNone: exceptionBlock | foundElement index each | index := 1. [foundElement isNil and: [index <= self size]] whileTrue: [ (aBlock value: (each := self at: index)) ifTrue: [foundElement := each]. index := index + 1]. ^ foundElement isNil ifTrue: [exceptionBlock value] ifFalse: [foundElement].
Hopefully someone can do better. As it stands it is much worse and I just don't know how to program in Squeak without non-local returns. It feels like there is a missing helper method in there or something. I don't know. At the point of detection we know we want to return that thing and the rest of this mathod just transfers it down to the end of the method. Noise.
Tell me what you think!
Cheers, Rob
Rob, ask this to a Compiler, an interpreter, but not to a Smalltalk programmer please!
Rob Withers a écrit :
I'll just throw this out and see what turns up. As you have probably heard, I am toying with adding eventual refs to Squeak. Unfortunately, they don't play well with non-local returns. Igor and I had been discussing what could be done with methods having non-local returns adn it is looking nasty. So I thought to look at another piece of the puzzle and question its existence.
How important is non-local return to Squeak? What would Squeak look like without it?
So I thought of the first use of it, detecting an object in a collection. Here is #detect:ifNone: with non-local return:
detect: aBlock ifNone: exceptionBlock self do: [:each | (aBlock value: each) ifTrue: [^ each]]. ^ exceptionBlock value
and here is a version without non-local return:
detectNoNonLocalReturn: aBlock ifNone: exceptionBlock | foundElement index each | index := 1. [foundElement isNil and: [index <= self size]] whileTrue: [ (aBlock value: (each := self at: index)) ifTrue: [foundElement := each]. index := index + 1]. ^ foundElement isNil ifTrue: [exceptionBlock value] ifFalse: [foundElement].
Hopefully someone can do better. As it stands it is much worse and I just don't know how to program in Squeak without non-local returns. It feels like there is a missing helper method in there or something. I don't know. At the point of detection we know we want to return that thing and the rest of this mathod just transfers it down to the end of the method. Noise.
Tell me what you think!
Cheers, Rob
Are you saying you couldn't do without this feature? I have to agree, but still the question is worth asking.
----- Original Message ----- From: "nicolas cellier" ncellier@ifrance.com To: squeak-dev@lists.squeakfoundation.org Sent: Tuesday, November 06, 2007 4:03 PM Subject: Re: What would Squeak be like without non-local returns
Rob, ask this to a Compiler, an interpreter, but not to a Smalltalk programmer please!
Rob Withers a écrit :
I'll just throw this out and see what turns up. As you have probably heard, I am toying with adding eventual refs to Squeak. Unfortunately, they don't play well with non-local returns. Igor and I had been discussing what could be done with methods having non-local returns adn it is looking nasty. So I thought to look at another piece of the puzzle and question its existence. How important is non-local return to Squeak? What would Squeak look like without it? So I thought of the first use of it, detecting an object in a collection. Here is #detect:ifNone: with non-local return: detect: aBlock ifNone: exceptionBlock self do: [:each | (aBlock value: each) ifTrue: [^ each]]. ^ exceptionBlock value and here is a version without non-local return: detectNoNonLocalReturn: aBlock ifNone: exceptionBlock | foundElement index each | index := 1. [foundElement isNil and: [index <= self size]] whileTrue: [ (aBlock value: (each := self at: index)) ifTrue: [foundElement := each]. index := index + 1]. ^ foundElement isNil ifTrue: [exceptionBlock value] ifFalse: [foundElement].
Hopefully someone can do better. As it stands it is much worse and I just don't know how to program in Squeak without non-local returns. It feels like there is a missing helper method in there or something. I don't know. At the point of detection we know we want to return that thing and the rest of this mathod just transfers it down to the end of the method. Noise. Tell me what you think! Cheers, Rob
Your example is very speaking. Searching such workaround and artefacts is contrary to Smalltalk spirit. The simpler, the better, since it enables focusing more on contents than on form.
Cheers
Rob Withers a écrit :
Are you saying you couldn't do without this feature? I have to agree, but still the question is worth asking.
----- Original Message ----- From: "nicolas cellier" ncellier@ifrance.com To: squeak-dev@lists.squeakfoundation.org Sent: Tuesday, November 06, 2007 4:03 PM Subject: Re: What would Squeak be like without non-local returns
Rob, ask this to a Compiler, an interpreter, but not to a Smalltalk programmer please!
Rob Withers a écrit :
I'll just throw this out and see what turns up. As you have probably heard, I am toying with adding eventual refs to Squeak. Unfortunately, they don't play well with non-local returns. Igor and I had been discussing what could be done with methods having non-local returns adn it is looking nasty. So I thought to look at another piece of the puzzle and question its existence. How important is non-local return to Squeak? What would Squeak look like without it? So I thought of the first use of it, detecting an object in a collection. Here is #detect:ifNone: with non-local return: detect: aBlock ifNone: exceptionBlock self do: [:each | (aBlock value: each) ifTrue: [^ each]]. ^ exceptionBlock value and here is a version without non-local return: detectNoNonLocalReturn: aBlock ifNone: exceptionBlock | foundElement index each | index := 1. [foundElement isNil and: [index <= self size]] whileTrue: [ (aBlock value: (each := self at: index)) ifTrue: [foundElement := each]. index := index + 1]. ^ foundElement isNil ifTrue: [exceptionBlock value] ifFalse: [foundElement].
Hopefully someone can do better. As it stands it is much worse and I just don't know how to program in Squeak without non-local returns. It feels like there is a missing helper method in there or something. I don't know. At the point of detection we know we want to return that thing and the rest of this mathod just transfers it down to the end of the method. Noise. Tell me what you think! Cheers, Rob
On 07/11/2007, Rob Withers reefedjib@yahoo.com wrote:
I'll just throw this out and see what turns up. As you have probably heard, I am toying with adding eventual refs to Squeak. Unfortunately, they don't play well with non-local returns. Igor and I had been discussing what could be done with methods having non-local returns adn it is looking nasty. So I thought to look at another piece of the puzzle and question its existence.
I don't think that their existence are questionable. Their have own purpose and in example you shown it proves that using non-local returns is much more convenient and easier for developer. If there are problems between exceptions/non-local returns and eventual refs, then they must be solved in one way or another but without sacrificing other language features. When i started my comments about need of special care with any stack unwinding operations i just wanted to point that you must take special care. It not that easy as removing non-local returns, but its solvable. So, i think, a better direction is to find a solution rather than looking how to put new feature by removing old one.
----- Original Message ----- From: "Igor Stasenko" siguctua@gmail.com To: "The general-purpose Squeak developers list" squeak-dev@lists.squeakfoundation.org Sent: Tuesday, November 06, 2007 4:19 PM Subject: Re: What would Squeak be like without non-local returns
On 07/11/2007, Rob Withers reefedjib@yahoo.com wrote:
I'll just throw this out and see what turns up. As you have probably heard, I am toying with adding eventual refs to Squeak. Unfortunately, they don't play well with non-local returns. Igor and I had been discussing what could be done with methods having non-local returns adn it is looking nasty. So I thought to look at another piece of the puzzle and question its existence.
I don't think that their existence are questionable. Their have own purpose and in example you shown it proves that using non-local returns is much more convenient and easier for developer.
Yeah, I agree. My brain is wired to think that way, cause if it is time to exit the method, then go ahead.
If there are problems between exceptions/non-local returns and eventual refs, then they must be solved in one way or another but without sacrificing other language features.
E states that non-local returns are bad and we are seeing th implications of that statement. Our candidate solution of mixing non-local returns and eventual refs is not pretty.
When i started my comments about need of special care with any stack unwinding operations i just wanted to point that you must take special care. It not that easy as removing non-local returns, but its solvable. So, i think, a better direction is to find a solution rather than looking how to put new feature by removing old one.
I wanted to consider the possibility. We shouldn't leave rocks unturned.
Knowing we have blocks in play within the execution of a method, we say we want to synchronize the execution of the method and not run it eventually. How do we do this?
Cheers, Rob
On 07/11/2007, Rob Withers reefedjib@yahoo.com wrote:
----- Original Message ----- From: "Igor Stasenko" siguctua@gmail.com To: "The general-purpose Squeak developers list" squeak-dev@lists.squeakfoundation.org Sent: Tuesday, November 06, 2007 4:19 PM Subject: Re: What would Squeak be like without non-local returns
On 07/11/2007, Rob Withers reefedjib@yahoo.com wrote:
I'll just throw this out and see what turns up. As you have probably heard, I am toying with adding eventual refs to Squeak. Unfortunately, they don't play well with non-local returns. Igor and I had been discussing what could be done with methods having non-local returns adn it is looking nasty. So I thought to look at another piece of the puzzle and question its existence.
I don't think that their existence are questionable. Their have own purpose and in example you shown it proves that using non-local returns is much more convenient and easier for developer.
Yeah, I agree. My brain is wired to think that way, cause if it is time to exit the method, then go ahead.
If there are problems between exceptions/non-local returns and eventual refs, then they must be solved in one way or another but without sacrificing other language features.
E states that non-local returns are bad and we are seeing th implications of that statement. Our candidate solution of mixing non-local returns and eventual refs is not pretty.
Yeah, but we talking about Smalltalk, not E. Maybe they are bad for E, but why they should be bad for smalltalk? :)
When i started my comments about need of special care with any stack unwinding operations i just wanted to point that you must take special care. It not that easy as removing non-local returns, but its solvable. So, i think, a better direction is to find a solution rather than looking how to put new feature by removing old one.
I wanted to consider the possibility. We shouldn't leave rocks unturned.
Knowing we have blocks in play within the execution of a method, we say we want to synchronize the execution of the method and not run it eventually. How do we do this?
Ok, lets look again on the problem. Consider a code: -- f := object future doSomeThing: someBlock. a := a + 1. f doSomeThingElse. --
Semantically, if block (someBlock) does non-local return, that means that next statements should not be evaluated at all means, because this could lead to unpredictable behavior.
But there is no ways how to prevent that: - any piece of code in #doSomeThing: can send a message to object(s) which lead to execution of non-local returns or throwing an exception causing stack unwinding.
This means that if you using futures/promises in your code, you can't make this code to be semantically equivalent as with not using them. In that way, i think, its better to think of how to prevent obvious pitfalls and give developer a clear ways how to use eventual refs safely or teach them (in some manner), that writing a code with futures is something different than writing same code in imperative fashion.
Igor Stasenko wrote:
On 07/11/2007, Rob Withers reefedjib@yahoo.com wrote:
E states that non-local returns are bad and we are seeing th implications of that statement. Our candidate solution of mixing non-local returns and eventual refs is not pretty.
Yeah, but we talking about Smalltalk, not E. Maybe they are bad for E, but why they should be bad for smalltalk? :)
Indeed. And as a matter of fact I can't recall that particular statement. Who said it and in what context? The main rule that matters to E (for many reasons) is about globally mutable state which is a complete no-no. But non-local returns? The only issue I can imagine with this is that code could conceivably "escape" from an evaluation which may make it harder for auditing but that's about all I can see.
Cheers, - Andreas
----- Original Message ----- From: "Andreas Raab" andreas.raab@gmx.de To: "The general-purpose Squeak developers list" squeak-dev@lists.squeakfoundation.org Sent: Tuesday, November 06, 2007 5:55 PM Subject: Re: What would Squeak be like without non-local returns
Igor Stasenko wrote:
On 07/11/2007, Rob Withers reefedjib@yahoo.com wrote:
E states that non-local returns are bad and we are seeing th implications of that statement. Our candidate solution of mixing non-local returns and eventual refs is not pretty.
Yeah, but we talking about Smalltalk, not E. Maybe they are bad for E, but why they should be bad for smalltalk? :)
Indeed. And as a matter of fact I can't recall that particular statement. Who said it and in what context? The main rule that matters to E (for many reasons) is about globally mutable state which is a complete no-no. But non-local returns? The only issue I can imagine with this is that code could conceivably "escape" from an evaluation which may make it harder for auditing but that's about all I can see.
Well, now I can't find that statement, but I was sure I had read it on erights or Mark's thesis. I searched and found nothing. It may have been a statement regarding eventual-sending specifically with non-local return. It IS strange thinking about the correct behavior of an expression:
object eventual ifTrue: [^3]. ^4
What should be the result?
What I did find was discussion about escape from a block with an ejector, which I had always wondered about. It is non-local return from a synchronous send. Click on ii.3 Escape: http://www.erights.org/elib/concurrency/msg-passing.html and also http://www.erights.org/elang/kernel/EscapeExpr.html
Cheers, Rob
It seems like everything after has to become eventual. But worse, some inst var could contain blocks with local return (see ifFail: [^nil] pre-exception-handling kind in Compiler).
object eventual ifTrue: [failBlock value]. ^4
Nothing obvious at compile time...
As already stated by Igor, you also have modern exception handling that could play tricks...
Nicolas
Rob Withers a écrit :
----- Original Message ----- From: "Andreas Raab" andreas.raab@gmx.de To: "The general-purpose Squeak developers list" squeak-dev@lists.squeakfoundation.org Sent: Tuesday, November 06, 2007 5:55 PM Subject: Re: What would Squeak be like without non-local returns
Igor Stasenko wrote:
On 07/11/2007, Rob Withers reefedjib@yahoo.com wrote:
E states that non-local returns are bad and we are seeing th implications of that statement. Our candidate solution of mixing non-local returns and eventual refs is not pretty.
Yeah, but we talking about Smalltalk, not E. Maybe they are bad for E, but why they should be bad for smalltalk? :)
Indeed. And as a matter of fact I can't recall that particular statement. Who said it and in what context? The main rule that matters to E (for many reasons) is about globally mutable state which is a complete no-no. But non-local returns? The only issue I can imagine with this is that code could conceivably "escape" from an evaluation which may make it harder for auditing but that's about all I can see.
Well, now I can't find that statement, but I was sure I had read it on erights or Mark's thesis. I searched and found nothing. It may have been a statement regarding eventual-sending specifically with non-local return. It IS strange thinking about the correct behavior of an expression:
object eventual ifTrue: [^3]. ^4
What should be the result?
What I did find was discussion about escape from a block with an ejector, which I had always wondered about. It is non-local return from a synchronous send. Click on ii.3 Escape: http://www.erights.org/elib/concurrency/msg-passing.html and also http://www.erights.org/elang/kernel/EscapeExpr.html
Cheers, Rob
On 07/11/2007, nicolas cellier ncellier@ifrance.com wrote:
It seems like everything after has to become eventual. But worse, some inst var could contain blocks with local return (see ifFail: [^nil] pre-exception-handling kind in Compiler).
object eventual ifTrue: [failBlock value]. ^4
Nothing obvious at compile time...
Hmm, this creates a non-deterministic behavoir each time we using non-local returns in futures. suppose we having execution flow: A / \ B C \ / D | E
Where A is our initial state where we divide our execution in two parallel threads B and C (one of them considered as future). Then, at some point we have both of them resolved and merged to single thread again (D). But in case of non-local return we actually should skip D, and jump directly to state E. The question is, if both B and C using non-local return, then which of them should be granted as a result of computation? One, which emerged from B, or from C? If we use 'first who is done - is winner' rule, then it makes our system very unpredictable, because due to scheduling there can be situations where either B or C can return first even given same input in algorithm. Ok, then we come to point, that if one of them are exited non-locally, we must wait for completion of another one, and only then decide which result to use.. But question is stays: which return result to use?
Rob Withers wrote:
Well, now I can't find that statement, but I was sure I had read it on erights or Mark's thesis. I searched and found nothing. It may have been a statement regarding eventual-sending specifically with non-local return. It IS strange thinking about the correct behavior of an expression:
object eventual ifTrue: [^3]. ^4
What should be the result?
Without any question, 4. Since ifTrue:[] is sent eventual it will return immediately (answering a promise), and the method will continue and return 4. There is absolutely no question about it.
When the eventual ifTrue: is executed it will barf because it will try to return from a method with no home but that's the way out-of-scope blocks go. It's just as well-defined as a non-local return from an out-of-scope block today, e.g.,
Foo>>createBlock ^[^self]
Foo>>callBlock self createBlock value.
This will simply signal BlockCannotReturn and the eventual ifTrue:[] will - and should - do the same.
Cheers, - Andreas
On 07/11/2007, Andreas Raab andreas.raab@gmx.de wrote:
Rob Withers wrote:
Well, now I can't find that statement, but I was sure I had read it on erights or Mark's thesis. I searched and found nothing. It may have been a statement regarding eventual-sending specifically with non-local return. It IS strange thinking about the correct behavior of an expression:
object eventual ifTrue: [^3]. ^4
What should be the result?
Without any question, 4. Since ifTrue:[] is sent eventual it will return immediately (answering a promise), and the method will continue and return 4. There is absolutely no question about it.
When the eventual ifTrue: is executed it will barf because it will try to return from a method with no home but that's the way out-of-scope blocks go. It's just as well-defined as a non-local return from an out-of-scope block today, e.g.,
Foo>>createBlock ^[^self]
Foo>>callBlock self createBlock value.
This will simply signal BlockCannotReturn and the eventual ifTrue:[] will - and should - do the same.
Let suppose that Mr.Satan will rewrite an example above as following:
object eventual ifTrue: [100 atRandom seconds asDelay wait. ^3]. 100 atRandom seconds asDelay wait. ^4
When, in real case, a random delay could be replaced by any code which needs a time to evaluate. What is the answer then? :)
Cheers,
- Andreas
Igor Stasenko wrote:
Let suppose that Mr.Satan will rewrite an example above as following:
object eventual ifTrue: [100 atRandom seconds asDelay wait. ^3]. 100 atRandom seconds asDelay wait. ^4
When, in real case, a random delay could be replaced by any code which needs a time to evaluate. What is the answer then? :)
Same answer (though strictly speaking you may get an error that you can't *wait* in an eventual system). The reason being that <object> will only get an eventual ref to the block so it will only be able to send other eventual messages to it. Put differently, ifTrue: would need to be implemented as:
True>>ifTrue: aBlock "I am true, so evaluate aBlock" aBlock isBlock "if it's an immediate block..." ifTrue:[aBlock value] "... evaluate it directly ..." ifFalse:[aBlock eventual value] "... otherwise eventual"
(I'm ignoring the issues of meta-circularity for ifTrue: here; the above is purely conceptual) The main point is that because the evaluation of the block would be eventual it would *necessarily* evaluate after the immediate delay and the entire message has completed *regardless* of how long or how short that delay is. If that were different it would violate one of the fundamental axioms of event-loop concurrency (the fact that only one message is ever executed at the time) and wouldn't count as message passing concurrency anymore.
Cheers, - Andreas
On 07/11/2007, Andreas Raab andreas.raab@gmx.de wrote:
Igor Stasenko wrote:
Let suppose that Mr.Satan will rewrite an example above as following:
object eventual ifTrue: [100 atRandom seconds asDelay wait. ^3]. 100 atRandom seconds asDelay wait. ^4
When, in real case, a random delay could be replaced by any code which needs a time to evaluate. What is the answer then? :)
Same answer (though strictly speaking you may get an error that you can't *wait* in an eventual system).
This doesn't change anything, you may put: (1 to: 1000000 atRandom) do: [1+1] instead of delay.
The reason being that <object> will only get an eventual ref to the block so it will only be able to send other eventual messages to it. Put differently, ifTrue: would need to be implemented as:
True>>ifTrue: aBlock "I am true, so evaluate aBlock" aBlock isBlock "if it's an immediate block..." ifTrue:[aBlock value] "... evaluate it directly ..." ifFalse:[aBlock eventual value] "... otherwise eventual"
(I'm ignoring the issues of meta-circularity for ifTrue: here; the above is purely conceptual) The main point is that because the evaluation of the block would be eventual it would *necessarily* evaluate after the immediate delay and the entire message has completed *regardless* of how long or how short that delay is. If that were different it would violate one of the fundamental axioms of event-loop concurrency (the fact that only one message is ever executed at the time) and wouldn't count as message passing concurrency anymore.
Hmm. An example above was to show that when running in parallel there is equal chance to get to ^3 or ^4 first. And in previous message you described behavior, when running code gets to ^4 first. But what if it gets to ^3 first? And if there no code running in parallel, then there's nothing to argue about.
Cheers,
- Andreas
Igor Stasenko wrote:
On 07/11/2007, Andreas Raab andreas.raab@gmx.de wrote:
Same answer (though strictly speaking you may get an error that you can't *wait* in an eventual system).
This doesn't change anything, you may put: (1 to: 1000000 atRandom) do: [1+1] instead of delay.
Sure. But it makes no difference. I was only pointing this out for completeness.
Hmm. An example above was to show that when running in parallel there is equal chance to get to ^3 or ^4 first.
No, there isn't. It's simply not how it works. This code cannot run in parallel because it's on the same island (necessarily so for non-shared state concurrency since blocks have access to receiver variables) and can therefore *not* be executed in parallel. The order is completely deterministic - scheduling the message in the future *guarantees* that it will be executed only after the ^4 has completed. It can't be any other way for it to work; if you'd want to run it in parallel you'd need to copy it to a different island first in which case the return value from the method would *still* be 4! It's completely deterministic, the result is 4.
And in previous message you described behavior, when running code gets to ^4 first. But what if it gets to ^3 first?
It cannot. It simply cannot. If you don't understand why, then you don't fully understand how E and Croquet works. The block is created in the same island as the receiver. When it is passed to another island (as argument to the future ifTrue: message) it will be converted into a remote reference. This reference only understands future messages (all the immediate messages relate entirely to its role as a reference not the underlying object) which is the reason why I distinguished these cases in the True>>ifTrue: code (simply because #value on a remote reference has no bearing on executing the block that this reference points to but scheduling a future value message does).
This in turn means that the second island (the one containing the boolean) *must schedule* the future #value with the first island which means that the first island will only execute it after the ^4 has been executed. It is the *only* way in which this can work in an event-loop environment.
What happens is something like here:
Island A: Island B: ->message starts running -> aBool future ifTrue:[] ..... -> ..... <schedules True>>ifTrue:> -> Delay wait
... time passes ... <at this point Island A is running the message> <and Island B has the #ifTrue: message scheduled>
-> executes True>>ifTrue: <schedules aBlock value> ..... <- ..... <- aBlock future value -> True>>ifTrue: finishes
.... time passes .... <Island A is still not finished with the delay> <Island B has posted the eventual #value to Island A>
-> Delay wait ends. -> message returns 4 -> message completes
<Now, and *only* now the pending #value call will be executed>
-> aBlock value starts -> Attempts to [^3] -> Raises BlockCannotReturn
The result is 4. Each and every time.
Cheers, - Andreas
Consider the code
object eventual: [... ^3] ... ^4
Depending on the missing code, it could return either 3 or 4. If not, something is very strange, and the language is no longer Smalltalk.
If you get rid of non-local returns, the language is no longer Smalltalk. it might be a good language, but it is not Smalltalk.
-Ralph
Hi Ralph,
That is not quite right.
What would this return?
[^3] fork. ^4.
(It is the same thing).
Ron Teitelbaum
-----Original Message----- From: squeak-dev-bounces@lists.squeakfoundation.org [mailto:squeak-dev- bounces@lists.squeakfoundation.org] On Behalf Of Ralph Johnson Sent: Wednesday, November 07, 2007 6:26 AM To: The general-purpose Squeak developers list Subject: Re: What would Squeak be like without non-local returns
Consider the code
object eventual: [... ^3] ... ^4
Depending on the missing code, it could return either 3 or 4. If not, something is very strange, and the language is no longer Smalltalk.
If you get rid of non-local returns, the language is no longer Smalltalk. it might be a good language, but it is not Smalltalk.
-Ralph
Ralph Johnson wrote:
Consider the code
object eventual: [... ^3] ... ^4
Depending on the missing code, it could return either 3 or 4. If not, something is very strange, and the language is no longer Smalltalk.
Not at all. Eventual/future sends introduce a new unit of concurrency and the only thing we're arguing is whether that second unit of concurrency will be executed before the first one. In E/Croquet this is not possible, but it is really no different from, e.g.,
Object>>foo [^3] forkAt: Processor activePriority-1. ^4
When you run this, it will return 4 (every time) and fall over the non-local return later (every time).
Cheers, - Andreas
On 11/7/07, Andreas Raab andreas.raab@gmx.de wrote:
Ralph Johnson wrote:
Consider the code
object eventual: [... ^3] ... ^4
Depending on the missing code, it could return either 3 or 4. If not, something is very strange, and the language is no longer Smalltalk.
Not at all. Eventual/future sends introduce a new unit of concurrency and the only thing we're arguing is whether that second unit of concurrency will be executed before the first one. In E/Croquet this is not possible, but it is really no different from, e.g.,
Object>>foo [^3] forkAt: Processor activePriority-1. ^4
When you run this, it will return 4 (every time) and fall over the non-local return later (every time).
But that wasn't what I said. Your code is different from mine. Note the ...
For example,
Object >>foo [^3] fork. self halt. ^ 4
returns 3.
On 08/11/2007, Ralph Johnson johnson@cs.uiuc.edu wrote:
On 11/7/07, Andreas Raab andreas.raab@gmx.de wrote:
Ralph Johnson wrote:
Consider the code
object eventual: [... ^3] ... ^4
Depending on the missing code, it could return either 3 or 4. If not, something is very strange, and the language is no longer Smalltalk.
Not at all. Eventual/future sends introduce a new unit of concurrency and the only thing we're arguing is whether that second unit of concurrency will be executed before the first one. In E/Croquet this is not possible, but it is really no different from, e.g.,
Object>>foo [^3] forkAt: Processor activePriority-1. ^4
When you run this, it will return 4 (every time) and fall over the non-local return later (every time).
But that wasn't what I said. Your code is different from mine. Note the ...
For example,
Object >>foo [^3] fork. self halt. ^ 4
returns 3.
This is all about same: what to do with two(or more) return values, where we expecting one.
----- Original Message ----- From: "Ralph Johnson" johnson@cs.uiuc.edu To: "The general-purpose Squeak developers list" squeak-dev@lists.squeakfoundation.org Sent: Wednesday, November 07, 2007 9:55 PM Subject: Re: What would Squeak be like without non-local returns
On 11/7/07, Andreas Raab andreas.raab@gmx.de wrote:
Ralph Johnson wrote:
Consider the code
object eventual: [... ^3] ... ^4
Depending on the missing code, it could return either 3 or 4. If not, something is very strange, and the language is no longer Smalltalk.
Not at all. Eventual/future sends introduce a new unit of concurrency and the only thing we're arguing is whether that second unit of concurrency will be executed before the first one. In E/Croquet this is not possible, but it is really no different from, e.g.,
Object>>foo [^3] forkAt: Processor activePriority-1. ^4
When you run this, it will return 4 (every time) and fall over the non-local return later (every time).
But that wasn't what I said. Your code is different from mine. Note the ...
For example,
Object >>foo [^3] fork. self halt. ^ 4
returns 3.
No, this halts, on the calling Process, and signals a BlockCannotReturn Error, on the forked Process, presumably because the returnContext is nil. Without the halt and with any number of statements (...), it would return 3.
Rob
Hi Ralph -
Sorry, I thought it was clear that I was giving conceptual answer. But if you need a literal one, here it is:
TestIsland>>initialize eventQueue := SharedQueue new. eventQueue nextPut:[self foo]. self runEventLoop.
TestIsland>>runEventLoop [true] whileTrue:[eventQueue next value]
TestIsland>>foo eventLoop nextPut:[^3]. self halt. ^4
This will result in 4 being returned from foo unless you introduce a different model of concurrency. In any case, neither of my examples qualifies for "the language is no longer Smalltalk" which was my point.
Cheers, - Andreas
Ralph Johnson wrote:
On 11/7/07, Andreas Raab andreas.raab@gmx.de wrote:
Ralph Johnson wrote:
Consider the code
object eventual: [... ^3] ... ^4
Depending on the missing code, it could return either 3 or 4. If not, something is very strange, and the language is no longer Smalltalk.
Not at all. Eventual/future sends introduce a new unit of concurrency and the only thing we're arguing is whether that second unit of concurrency will be executed before the first one. In E/Croquet this is not possible, but it is really no different from, e.g.,
Object>>foo [^3] forkAt: Processor activePriority-1. ^4
When you run this, it will return 4 (every time) and fall over the non-local return later (every time).
But that wasn't what I said. Your code is different from mine. Note the ...
For example,
Object >>foo [^3] fork. self halt. ^ 4
returns 3.
Hi Andreas
I would like to learn about this Croquet futures business. And in so doing I would like to try to document it better. Is this possible? Where can I look for information? Are there any isolated tests that I could step through? Is there anyone who would be willing to teach me edit me? Where could I put the new documentation? I think the best way to teach people how futures work inside would be to construct a sequence of tests that progress from simple to more complex and that can be run on a single machine. By stepping through these tests and documenting the methods that show up along the way the implementation details can be shown. By putting each of the common usages into a test you can also show how futures should be used from a user's point of view starting with simple usage and moving on to complex usage. Such tests would not need to be single stepped if the only purpose was to learn usages. If the implementation was to be learned then stepping could be done. I give an example of how such things could work in the picoLARC project on sourceforge.net for Dolphin Smalltalk community edition 6. You make a sequence of tests that run one after the other. These tests document usage and implementation in an active way that doesn't go out of date.
Most people hate to make documentation. I do not. I like it.
What do you think? Where is the info I need? How can I upload the documentation that I make? How can I upload new test sequences and the extra method documentations I will put into the methods that that show up along the way? Who will edit me?
-Kjell picoVerse
by the way there are a lot of ambiguous terms below that I would like to make more concrete in the documentation I attempt to make.
On Nov 7, 2007 12:31 AM, Andreas Raab andreas.raab@gmx.de wrote:
Igor Stasenko wrote:
On 07/11/2007, Andreas Raab andreas.raab@gmx.de wrote:
Same answer (though strictly speaking you may get an error that you can't *wait* in an eventual system).
This doesn't change anything, you may put: (1 to: 1000000 atRandom) do: [1+1] instead of delay.
Sure. But it makes no difference. I was only pointing this out for completeness.
Hmm. An example above was to show that when running in parallel there is equal chance to get to ^3 or ^4 first.
No, there isn't. It's simply not how it works. This code cannot run in parallel because it's on the same island (necessarily so for non-shared state concurrency since blocks have access to receiver variables) and can therefore *not* be executed in parallel. The order is completely deterministic - scheduling the message in the future *guarantees* that it will be executed only after the ^4 has completed. It can't be any other way for it to work; if you'd want to run it in parallel you'd need to copy it to a different island first in which case the return value from the method would *still* be 4! It's completely deterministic, the result is 4.
And in previous message you described behavior, when running code gets to ^4 first. But what if it gets to ^3 first?
It cannot. It simply cannot. If you don't understand why, then you don't fully understand how E and Croquet works. The block is created in the same island as the receiver. When it is passed to another island (as argument to the future ifTrue: message) it will be converted into a remote reference. This reference only understands future messages (all the immediate messages relate entirely to its role as a reference not the underlying object) which is the reason why I distinguished these cases in the True>>ifTrue: code (simply because #value on a remote reference has no bearing on executing the block that this reference points to but scheduling a future value message does).
This in turn means that the second island (the one containing the boolean) *must schedule* the future #value with the first island which means that the first island will only execute it after the ^4 has been executed. It is the *only* way in which this can work in an event-loop environment.
What happens is something like here:
Island A: Island B: ->message starts running -> aBool future ifTrue:[] ..... -> ..... <schedules True>>ifTrue:> -> Delay wait
... time passes ... <at this point Island A is running the message> <and Island B has the #ifTrue: message scheduled> -> executes True>>ifTrue:
<schedules aBlock value> ..... <- ..... <- aBlock future value -> True>>ifTrue: finishes
.... time passes .... <Island A is still not finished with the delay> <Island B has posted the eventual #value to Island A>
-> Delay wait ends. -> message returns 4 -> message completes
<Now, and *only* now the pending #value call will be executed>
-> aBlock value starts -> Attempts to [^3] -> Raises BlockCannotReturn
The result is 4. Each and every time.
Cheers,
- Andreas
----- Original Message ----- From: "Andreas Raab" andreas.raab@gmx.de To: "The general-purpose Squeak developers list" squeak-dev@lists.squeakfoundation.org Sent: Tuesday, November 06, 2007 9:38 PM Subject: Re: What would Squeak be like without non-local returns
Rob Withers wrote:
Well, now I can't find that statement, but I was sure I had read it on erights or Mark's thesis. I searched and found nothing. It may have been a statement regarding eventual-sending specifically with non-local return. It IS strange thinking about the correct behavior of an expression:
object eventual ifTrue: [^3]. ^4
What should be the result?
Without any question, 4. Since ifTrue:[] is sent eventual it will return immediately (answering a promise), and the method will continue and return 4. There is absolutely no question about it.
When the eventual ifTrue: is executed it will barf because it will try to return from a method with no home but that's the way out-of-scope blocks go. It's just as well-defined as a non-local return from an out-of-scope block today, e.g.,
Foo>>createBlock ^[^self]
Foo>>callBlock self createBlock value.
This will simply signal BlockCannotReturn and the eventual ifTrue:[] will - and should - do the same.
This makes that percieved problem of non-local returns with eventual sending just go away. I am totally convinced.
The only difference I have at the moment, is that instead of getting an unhandled BlockCannotReturn exception thrown, SqueakElib instead catches the exception and resolves the promise for the eventually send #ifTrue: to be a broken ref. Since that promise was never utilized, it quietly goes away - it never opens the notifier. I am torn whether this is the correct thing to do.
Cheers, Rob
Cheers,
- Andreas
----- Original Message ----- From: "Igor Stasenko" siguctua@gmail.com
Knowing we have blocks in play within the execution of a method, we say we want to synchronize the execution of the method and not run it eventually. How do we do this?
Ok, lets look again on the problem. Consider a code: -- f := object future doSomeThing: someBlock. a := a + 1. f doSomeThingElse. --
Semantically, if block (someBlock) does non-local return, that means that next statements should not be evaluated at all means, because this could lead to unpredictable behavior.
Does it mean this? Because this violates the rule that there should be no blocking. Msgs should just be sent. Maybe the rule in play is that someBlock does a non-local return that is no longer valid, because it was scheduled eventually outside the scope of the method it could return from, so it should cause an Error and the return value of doSomeThing: is a broken ref. A way out of this pickle?
But there is no ways how to prevent that:
- any piece of code in #doSomeThing: can send a message to object(s)
which lead to execution of non-local returns or throwing an exception causing stack unwinding.
Ok, but if what I just said holds, then the unwinding terminates where the msg was eventually sent and never gets to interact with the sending context.
This means that if you using futures/promises in your code, you can't make this code to be semantically equivalent as with not using them.
I think that's true.
In that way, i think, its better to think of how to prevent obvious pitfalls and give developer a clear ways how to use eventual refs safely or teach them (in some manner), that writing a code with futures is something different than writing same code in imperative fashion.
Yes, it is not transparent. On the other hand, any ref could be eventual. So the rules for how they behave in various imperative situations are important. As to how to write our example clearly, in the eventual style:
f := object future doSomeThing: someBlock. f whenResolved: [:fVow | a := a + 1. fVow doSomeThingElse].
Rob
I think you guys are over complicating this. Return to a home context from a future doesn't make any sense and should just toss an error, as returning to a home context that no longer exist does now.
E style futures are basically some code asking a process somewhere else to do something, and giving you the ability to attach a callback for when the other process is finished. I don't think it make sense to have the remote code return to some context that isn't in it's address space, and allowing home context returns in the callback is certainly a bad idea, as the home context should not still be around when the callback runs.
On Nov 7, 2007 5:00 AM, Rob Withers reefedjib@yahoo.com wrote:
----- Original Message ----- From: "Igor Stasenko" siguctua@gmail.com
Knowing we have blocks in play within the execution of a method, we say we want to synchronize the execution of the method and not run it eventually. How do we do this?
Ok, lets look again on the problem. Consider a code: -- f := object future doSomeThing: someBlock. a := a + 1. f doSomeThingElse. --
Semantically, if block (someBlock) does non-local return, that means that next statements should not be evaluated at all means, because this could lead to unpredictable behavior.
Does it mean this? Because this violates the rule that there should be no blocking. Msgs should just be sent. Maybe the rule in play is that someBlock does a non-local return that is no longer valid, because it was scheduled eventually outside the scope of the method it could return from, so it should cause an Error and the return value of doSomeThing: is a broken ref. A way out of this pickle?
But there is no ways how to prevent that:
- any piece of code in #doSomeThing: can send a message to object(s)
which lead to execution of non-local returns or throwing an exception causing stack unwinding.
Ok, but if what I just said holds, then the unwinding terminates where the msg was eventually sent and never gets to interact with the sending context.
This means that if you using futures/promises in your code, you can't make this code to be semantically equivalent as with not using them.
I think that's true.
In that way, i think, its better to think of how to prevent obvious pitfalls and give developer a clear ways how to use eventual refs safely or teach them (in some manner), that writing a code with futures is something different than writing same code in imperative fashion.
Yes, it is not transparent. On the other hand, any ref could be eventual. So the rules for how they behave in various imperative situations are important. As to how to write our example clearly, in the eventual style:
f := object future doSomeThing: someBlock. f whenResolved: [:fVow | a := a + 1. fVow doSomeThingElse].
Rob
----- Original Message ----- From: "Mathieu Suen" mathk.sue@gmail.com To: "The general-purpose Squeak developers list" squeak-dev@lists.squeakfoundation.org Sent: Tuesday, November 06, 2007 5:10 PM Subject: Re: What would Squeak be like without non-local returns
eventual refs?
This is what I am hoping to build: http://wiki.squeak.org/squeak/6011 This also describes some of what I was trying to do: http://wiki.squeak.org/squeak/2410
The canonical source for this is here: http://www.erights.org. I am reading Mark Miller's thesis, which is excellent.
Rob
By the way I have a suggestion for the #class and #==. Instead of building a new VM you could patch the compiler to avoid emitting the bytecodePrimEquals... So people can just use you code without having a compiled VM.
HTH Mth
On Nov 7, 2007, at 2:13 AM, Rob Withers wrote:
----- Original Message ----- From: "Mathieu Suen" mathk.sue@gmail.com To: "The general-purpose Squeak developers list" <squeak- dev@lists.squeakfoundation.org> Sent: Tuesday, November 06, 2007 5:10 PM Subject: Re: What would Squeak be like without non-local returns
eventual refs?
This is what I am hoping to build: http://wiki.squeak.org/squeak/6011 This also describes some of what I was trying to do: http:// wiki.squeak.org/squeak/2410
The canonical source for this is here: http://www.erights.org. I am reading Mark Miller's thesis, which is excellent.
Rob
----- Original Message ----- From: "Mathieu Suen" mathk.sue@gmail.com To: "The general-purpose Squeak developers list" squeak-dev@lists.squeakfoundation.org Sent: Wednesday, November 07, 2007 3:42 AM Subject: Re: What would Squeak be like without non-local returns
By the way I have a suggestion for the #class and #==. Instead of building a new VM you could patch the compiler to avoid emitting the bytecodePrimEquals... So people can just use you code without having a compiled VM.
I have actually decided that #== and #class are protocol belonging to the reference and not to be eventually sent. So I disabled the VM changes I made in this regard. I see now that I am more familiar with the Compiler, that I could patch the compiler to make these msg sends, rather than bytecodes and if I change my mind about these, that is definitely that approach to take.
There is a need for an #eventuallyEquals: operation, when the distributed stuff is added back in to the framework. Object a holds Object b from Vat B, and Object c calls Object a with a remote reference to Object b, called b'. b should equal b'.
Since I no longer need vm changes for #== and #class, the only current changes I have are for the Larger Contexts (80) and for the extra long jumps, added to support the combined macros I added to the compiler for #ifTrue:, #whileTrue: and so on. I still haven't gotten those changes to be stable.
As the roadmap on http://wiki.squeak.org/squeak/6011 describes, I plan on protecting primitives to allow them to have eventual arguments. This would entail vm changes. Finally, supporting messaging between vats may entail vm changes, which I attempted last week but failed. I tried to add a word to every object header, but it kept crashing on me.
So, in summary, vm changes are possible for: - support Compiler changes for combined macros - protecting Primitives from eventual arguments - support messaging between multiple vats
Cheers, Rob
squeak-dev@lists.squeakfoundation.org