A new version of Kernel was added to project The Inbox: http://source.squeak.org/inbox/Kernel-tonyg.1148.mcz
==================== Summary ====================
Name: Kernel-tonyg.1148 Author: tonyg Time: 31 January 2018, 11:37:21.27021 pm UUID: 2a675301-406b-4222-9599-84358a7cc506 Ancestors: Kernel-eem.1147
Brings the Promise implementation closer to Javascript/A+ promises in two ways: * Resolving a Promise with another Promise causes them to be chained together * Resolving or Rejecting a Promise that is not pending is a no-op, like the Firefox/Chrome/etc in-browser promise implementations. The tests have been changed in KernelTests-tonyg.331 accordingly.
=============== Diff against Kernel-eem.1147 ===============
Item was changed: Object subclass: #Promise + instanceVariableNames: 'value resolvers mutex state error rejecters' - instanceVariableNames: 'onError value resolvers mutex state error rejectors rejecters' classVariableNames: '' poolDictionaries: '' category: 'Kernel-Processes'!
+ !Promise commentStamp: 'tonyg 1/31/2018 23:34' prior: 0! - !Promise commentStamp: 'fbs 5/17/2013 18:23' prior: 0! I represent the result of an asynchronous message. Once the message is processed, I will be resolved to a value. I am typically instantiated by invocations of #futureSend:at:args: (and not by #futureDo:atArgs:).
See class-comment of FutureNode.
I also implement the Promises/A+ Javascript specification. This allows you to chain my instances to perform arbitrarily complex asynchronous tasks with error handling baked in.
+ A Promise may be in one of three possible states: #pending, #fulfilled or #rejected. A Promise may move from #pending -> #fulfilled (by way of the resolveWith: message), or from #pending -> #rejected (by way of rejectWith:). No other state changes may occur. + + Once #fulfilled or #rejected, a Promise's value must not change. In keeping with the major Javascript Promise implementations' interpretations of this, calls to resolveWith: or rejectWith: when a Promise is in #fulfilled or #rejected state are simply ignored - an error is not signalled. (See test cases PromiseTest testFirstResolutionWins, testCannotRejectFulfilledPromise and testCannotResolveaRejectedPromise.)! - A Promise may be in one of three possible states: #pending, #fulfilled or #rejected. A Promise may move from #pending -> #fulfilled, or from #pending -> #rejected. No other state changes may occur. Once #fulfilled or #rejected, a Promise's value must change.!
Item was changed: ----- Method: Promise>>rejectWith: (in category 'resolving') ----- rejectWith: anObject "Reject this promise." mutex critical: [ + (state == #pending) ifTrue: [ + error := anObject. + state := #rejected. + rejecters do: [:r | self evaluateRejecter: r]]]! - (state == #fulfilled) ifTrue: [self error: 'Promise was already resolved']. - (state == #rejected) ifTrue: [self error: 'Promise was already rejected']. - error := anObject. - state := #rejected. - rejecters do: [:r | self evaluateRejecter: r]].!
Item was changed: ----- Method: Promise>>resolveWith: (in category 'resolving') ----- resolveWith: arg + "Resolve this promise. If arg is itself a Promise, make this promise depend upon it, + as detailed in the Promises/A+ spec: + https://promisesaplus.com/#the-promise-resolution-procedure" + + (arg isKindOf: Promise) + ifTrue: [ + arg whenResolved: [:v | self resolveWith: v]. + arg whenRejected: [:e | self rejectWith: e]] + ifFalse: [ + mutex critical: [ + (state == #pending) ifTrue: [ + value := arg. + state := #fulfilled. + resolvers do: [:r | self evaluateResolver: r]]]]! - "Resolve this promise" - mutex critical: [ - (state == #fulfilled) ifTrue: [self error: 'Promise was already resolved']. - (state == #rejected) ifTrue: [self error: 'Promise was already resolved']. - value := arg. - state := #fulfilled. - resolvers do: [:r | - self evaluateResolver: r]].!
Item was changed: ----- Method: Promise>>then: (in category 'monad') ----- then: resolvedBlock + ^ self then: resolvedBlock ifRejected: [:e | "Pass rejection reason along" e].! - ^ self then: resolvedBlock ifRejected: [:ignored | "Do nothing"].!
Hi Tony,
On Wed, Jan 31, 2018 at 3:37 PM, commits@source.squeak.org wrote:
A new version of Kernel was added to project The Inbox: http://source.squeak.org/inbox/Kernel-tonyg.1148.mcz
==================== Summary ====================
Name: Kernel-tonyg.1148 Author: tonyg Time: 31 January 2018, 11:37:21.27021 pm UUID: 2a675301-406b-4222-9599-84358a7cc506 Ancestors: Kernel-eem.1147
Brings the Promise implementation closer to Javascript/A+ promises in two ways: * Resolving a Promise with another Promise causes them to be chained together * Resolving or Rejecting a Promise that is not pending is a no-op, like the Firefox/Chrome/etc in-browser promise implementations. The tests have been changed in KernelTests-tonyg.331 accordingly.
=============== Diff against Kernel-eem.1147 ===============
Item was changed: Object subclass: #Promise
instanceVariableNames: 'value resolvers mutex state error
rejecters'
instanceVariableNames: 'onError value resolvers mutex state error
rejectors rejecters' classVariableNames: '' poolDictionaries: '' category: 'Kernel-Processes'!
- !Promise commentStamp: 'tonyg 1/31/2018 23:34' prior: 0!
- !Promise commentStamp: 'fbs 5/17/2013 18:23' prior: 0! I represent the result of an asynchronous message. Once the message is
processed, I will be resolved to a value. I am typically instantiated by invocations of #futureSend:at:args: (and not by #futureDo:atArgs:).
See class-comment of FutureNode.
I also implement the Promises/A+ Javascript specification. This allows you to chain my instances to perform arbitrarily complex asynchronous tasks with error handling baked in.
- A Promise may be in one of three possible states: #pending, #fulfilled
or #rejected. A Promise may move from #pending -> #fulfilled (by way of the resolveWith: message), or from #pending -> #rejected (by way of rejectWith:). No other state changes may occur.
- Once #fulfilled or #rejected, a Promise's value must not change. In
keeping with the major Javascript Promise implementations' interpretations of this, calls to resolveWith: or rejectWith: when a Promise is in #fulfilled or #rejected state are simply ignored - an error is not signalled. (See test cases PromiseTest testFirstResolutionWins, testCannotRejectFulfilledPromise and testCannotResolveaRejectedPromise.)!
- A Promise may be in one of three possible states: #pending, #fulfilled
or #rejected. A Promise may move from #pending -> #fulfilled, or from #pending -> #rejected. No other state changes may occur. Once #fulfilled or #rejected, a Promise's value must change.!
Item was changed: ----- Method: Promise>>rejectWith: (in category 'resolving') ----- rejectWith: anObject "Reject this promise." mutex critical: [
(state == #pending) ifTrue: [
error := anObject.
state := #rejected.
rejecters do: [:r | self evaluateRejecter: r]]]!
(state == #fulfilled) ifTrue: [self error: 'Promise was
already resolved'].
(state == #rejected) ifTrue: [self error: 'Promise was
already rejected'].
error := anObject.
state := #rejected.
rejecters do: [:r | self evaluateRejecter: r]].!
Item was changed: ----- Method: Promise>>resolveWith: (in category 'resolving') ----- resolveWith: arg
"Resolve this promise. If arg is itself a Promise, make this
promise depend upon it,
as detailed in the Promises/A+ spec:
https://promisesaplus.com/#the-promise-resolution-
procedure"
(arg isKindOf: Promise)
I'd /much/ rather see Object>>isPromise and Promise>>isPromise than this.
ifTrue: [
arg whenResolved: [:v | self resolveWith: v].
arg whenRejected: [:e | self rejectWith: e]]
ifFalse: [
mutex critical: [
(state == #pending) ifTrue: [
value := arg.
state := #fulfilled.
resolvers do: [:r | self
evaluateResolver: r]]]]!
"Resolve this promise"
mutex critical: [
(state == #fulfilled) ifTrue: [self error: 'Promise was
already resolved'].
(state == #rejected) ifTrue: [self error: 'Promise was
already resolved'].
value := arg.
state := #fulfilled.
resolvers do: [:r |
self evaluateResolver: r]].!
Item was changed: ----- Method: Promise>>then: (in category 'monad') ----- then: resolvedBlock
^ self then: resolvedBlock ifRejected: [:e | "Pass rejection
reason along" e].!
^ self then: resolvedBlock ifRejected: [:ignored | "Do nothing"].!
_,,,^..^,,,_ best, Eliot
Hi Eliot,
On 02/01/2018 12:34 AM, Eliot Miranda wrote:
+ (arg isKindOf: Promise)
I'd /much/ rather see Object>>isPromise and Promise>>isPromise than this.
Excellent point! Thanks.
There is a Promise>>isPromise, but no matching Object>>isPromise.
Kernel-tonyg.1150 (just committed to Inbox) reintroduces it, and switches from using isKindOf: to using isPromise.
This change is also in keeping with the spirit of Promises/A+, where anything that is "thenable" is treated as promise-like.
Tony
Tony has a set of inbox submissions in Kernel, Multilingual, and MultilingualTests that appear to be ready to move to trunk.
Should we move these to trunk? If no objections I will move them a few days from now if no one else has gotten to it by then.
There will be some new test failures for UTF8EdgeCaseTest, which is a good thing since these are new tests that document existing issues.
Dave
On Thu, Feb 01, 2018 at 10:02:22AM +0000, Tony Garnock-Jones wrote:
Hi Eliot,
On 02/01/2018 12:34 AM, Eliot Miranda wrote:
+?? ?? ?? ??(arg isKindOf: Promise)
I'd /much/ rather see Object>>isPromise and Promise>>isPromise than this.
Excellent point! Thanks.
There is a Promise>>isPromise, but no matching Object>>isPromise.
Kernel-tonyg.1150 (just committed to Inbox) reintroduces it, and switches from using isKindOf: to using isPromise.
This change is also in keeping with the spirit of Promises/A+, where anything that is "thenable" is treated as promise-like.
Tony
On 02.02.2018, at 04:20, David T. Lewis lewis@mail.msen.com wrote:
Tony has a set of inbox submissions in Kernel, Multilingual, and MultilingualTests that appear to be ready to move to trunk.
Should we move these to trunk? If no objections I will move them a few days from now if no one else has gotten to it by then.
There will be some new test failures for UTF8EdgeCaseTest, which is a good thing since these are new tests that document existing issues.
Yes please
Dave
On Thu, Feb 01, 2018 at 10:02:22AM +0000, Tony Garnock-Jones wrote:
Hi Eliot,
On 02/01/2018 12:34 AM, Eliot Miranda wrote:
+?? ?? ?? ??(arg isKindOf: Promise)
I'd /much/ rather see Object>>isPromise and Promise>>isPromise than this.
Excellent point! Thanks.
There is a Promise>>isPromise, but no matching Object>>isPromise.
Kernel-tonyg.1150 (just committed to Inbox) reintroduces it, and switches from using isKindOf: to using isPromise.
This change is also in keeping with the spirit of Promises/A+, where anything that is "thenable" is treated as promise-like.
Tony
+1 for #isPromise
I don't think we need the #isKindOf: "flavor" here because the basic concept seems to be quite mature and thus justifies the addition to the Object protocol.
(For other discussions about #isKindOf: see the history of the mailing list. There was at least one in combination with Morphic Flaps.)
Best, Marcel Am 01.02.2018 01:34:33 schrieb Eliot Miranda eliot.miranda@gmail.com: Hi Tony,
On Wed, Jan 31, 2018 at 3:37 PM, <commits@source.squeak.org [mailto:commits@source.squeak.org]> wrote:
A new version of Kernel was added to project The Inbox: http://source.squeak.org/inbox/Kernel-tonyg.1148.mcz [http://source.squeak.org/inbox/Kernel-tonyg.1148.mcz]
==================== Summary ====================
Name: Kernel-tonyg.1148 Author: tonyg Time: 31 January 2018, 11:37:21.27021 pm UUID: 2a675301-406b-4222-9599-84358a7cc506 Ancestors: Kernel-eem.1147
Brings the Promise implementation closer to Javascript/A+ promises in two ways: * Resolving a Promise with another Promise causes them to be chained together * Resolving or Rejecting a Promise that is not pending is a no-op, like the Firefox/Chrome/etc in-browser promise implementations. The tests have been changed in KernelTests-tonyg.331 accordingly.
=============== Diff against Kernel-eem.1147 ===============
Item was changed: Object subclass: #Promise + instanceVariableNames: 'value resolvers mutex state error rejecters' - instanceVariableNames: 'onError value resolvers mutex state error rejectors rejecters' classVariableNames: '' poolDictionaries: '' category: 'Kernel-Processes'!
+ !Promise commentStamp: 'tonyg 1/31/2018 23:34' prior: 0! - !Promise commentStamp: 'fbs 5/17/2013 18:23' prior: 0! I represent the result of an asynchronous message. Once the message is processed, I will be resolved to a value. I am typically instantiated by invocations of #futureSend:at:args: (and not by #futureDo:atArgs:).
See class-comment of FutureNode.
I also implement the Promises/A+ Javascript specification. This allows you to chain my instances to perform arbitrarily complex asynchronous tasks with error handling baked in.
+ A Promise may be in one of three possible states: #pending, #fulfilled or #rejected. A Promise may move from #pending -> #fulfilled (by way of the resolveWith: message), or from #pending -> #rejected (by way of rejectWith:). No other state changes may occur. + + Once #fulfilled or #rejected, a Promise's value must not change. In keeping with the major Javascript Promise implementations' interpretations of this, calls to resolveWith: or rejectWith: when a Promise is in #fulfilled or #rejected state are simply ignored - an error is not signalled. (See test cases PromiseTest testFirstResolutionWins, testCannotRejectFulfilledPromise and testCannotResolveaRejectedPromise.)! - A Promise may be in one of three possible states: #pending, #fulfilled or #rejected. A Promise may move from #pending -> #fulfilled, or from #pending -> #rejected. No other state changes may occur. Once #fulfilled or #rejected, a Promise's value must change.!
Item was changed: ----- Method: Promise>>rejectWith: (in category 'resolving') ----- rejectWith: anObject "Reject this promise." mutex critical: [ + (state == #pending) ifTrue: [ + error := anObject. + state := #rejected. + rejecters do: [:r | self evaluateRejecter: r]]]! - (state == #fulfilled) ifTrue: [self error: 'Promise was already resolved']. - (state == #rejected) ifTrue: [self error: 'Promise was already rejected']. - error := anObject. - state := #rejected. - rejecters do: [:r | self evaluateRejecter: r]].!
Item was changed: ----- Method: Promise>>resolveWith: (in category 'resolving') ----- resolveWith: arg + "Resolve this promise. If arg is itself a Promise, make this promise depend upon it, + as detailed in the Promises/A+ spec: + https://promisesaplus.com/#the-promise-resolution-procedure [https://promisesaplus.com/#the-promise-resolution-procedure]" + + (arg isKindOf: Promise)
I'd /much/ rather see Object>>isPromise and Promise>>isPromise than this. + ifTrue: [ + arg whenResolved: [:v | self resolveWith: v]. + arg whenRejected: [:e | self rejectWith: e]] + ifFalse: [ + mutex critical: [ + (state == #pending) ifTrue: [ + value := arg. + state := #fulfilled. + resolvers do: [:r | self evaluateResolver: r]]]]! - "Resolve this promise" - mutex critical: [ - (state == #fulfilled) ifTrue: [self error: 'Promise was already resolved']. - (state == #rejected) ifTrue: [self error: 'Promise was already resolved']. - value := arg. - state := #fulfilled. - resolvers do: [:r | - self evaluateResolver: r]].!
Item was changed: ----- Method: Promise>>then: (in category 'monad') ----- then: resolvedBlock + ^ self then: resolvedBlock ifRejected: [:e | "Pass rejection reason along" e].! - ^ self then: resolvedBlock ifRejected: [:ignored | "Do nothing"].!
_,,,^..^,,,_
best, Eliot
squeak-dev@lists.squeakfoundation.org