A new version of KernelTests was added to project The Inbox:
http://source.squeak.org/inbox/KernelTests-jr.383.mcz ==================== Summary ==================== Name: KernelTests-jr.383 Author: jr Time: 21 June 2020, 7:27:47.560506 pm UUID: ecddb3cb-25c9-6c43-a879-59e527a3a9ee Ancestors: KernelTests-tonyg.382 Test whether Notifications reject the promises of future sends In my opinion, they should not. =============== Diff against KernelTests-tonyg.382 =============== Item was added: + ----- Method: PromiseTest>>testFutureResolutionWithNotification (in category 'tests - future') ----- + testFutureResolutionWithNotification + | p | + p := [Notification signal: 'should not reject'. 3 + 4] future value. + self assert: (self waitUntil: [p isResolved or: [p isRejected]] orCycleCount: 100). + self assert: p isResolved. + self assert: 7 equals: p value.! |
I am trying to come up with a nice solution for this failing test.
Am So., 21. Juni 2020 um 19:27 Uhr schrieb <[hidden email]>: > > A new version of KernelTests was added to project The Inbox: > http://source.squeak.org/inbox/KernelTests-jr.383.mcz > > ==================== Summary ==================== > > Name: KernelTests-jr.383 > Author: jr > Time: 21 June 2020, 7:27:47.560506 pm > UUID: ecddb3cb-25c9-6c43-a879-59e527a3a9ee > Ancestors: KernelTests-tonyg.382 > > Test whether Notifications reject the promises of future sends > > In my opinion, they should not. > > =============== Diff against KernelTests-tonyg.382 =============== > > Item was added: > + ----- Method: PromiseTest>>testFutureResolutionWithNotification (in category 'tests - future') ----- > + testFutureResolutionWithNotification > + | p | > + p := [Notification signal: 'should not reject'. 3 + 4] future value. > + self assert: (self waitUntil: [p isResolved or: [p isRejected]] orCycleCount: 100). > + self assert: p isResolved. > + self assert: 7 equals: p value.! > > |
Hi Jakob, Unrelated to your concern about Notifications, I notice the test assigns p to the result of sending #value to the Promise, which will very possibly still be nil at that point, even though it then goes on to test it against #isResolved and #isRejected right afterward. I find the impedance mismatch between values and Promises of values confusing, too, -- afterall, I thought the purpose was to be able to use them interchangeably. Equally, I wish 'error' were always some kind of Exception, and would signal it when #value (wait) was sent instead of exposing Promises by forcing clients to ask, #isRejected? #isResolved? - Chris On Sun, Jun 21, 2020 at 12:46 PM Jakob Reschke <[hidden email]> wrote: I am trying to come up with a nice solution for this failing test. |
Hi Chris,
I was sometimes confused about this too: future itself does not return a Promise, but future + the following message send does. This is due to the compiler transformation explained in FutureNode. So value will not be sent to the Promise and p will hold the Promise right away and not be nil. So, I agree that #future is confusing, but it is also much less to read than p := Promise new. Project current addDeferredUIMessage: [[... p resolveWith: ...] ifCurtailed: "or whatever we settle on" [p reject]]. I don't think you ever could use Promises and values interchangeably. Promise>>wait does more or less what you want, but it means you have to use the promise explicitly, not interchangeably. You can chain promises with then:[ifRejected:], and consume the Promise outcome in the respective block argument, turning your control flow from left-to-right-top-to-down into Promise style... But just getting the value of the Promise at some point without wait might just answer nil if still unresolved. About signalling errors upon #value as you put it: you mean you want to get the original Exception when you get the result of the promise, so you don't have to check in which state the promise is? It sounds useful at first, but the result or reason (for errors) can be retrieved more than once; would the exception only be signalled (again) upon the first retrieval of the result? Also it would look funny in the Debugger stack: either you put the original stack of the exception on top of the current (result getting) one, or you hide the current stack, or you cut away the sender of the exception context (which makes it less useful). Do you have an idea how to resolve this? Also note that you can reject a promise with something else than an Exception. It could also be some kind of error value or just a string that explains the rejection. You would not be able to distinguish that from a regular result without type checking. Maybe we could have a specialized promise that only rejects on exceptions and would allow the kind of workflow you seem to have in mind where the promise is like a proxy for the result. If we determine that it is useful. :-) Kind regards, Jakob Am Do., 25. Juni 2020 um 02:55 Uhr schrieb Chris Muller <[hidden email]>: > > Hi Jakob, > > Unrelated to your concern about Notifications, I notice the test assigns p to the result of sending #value to the Promise, which will very possibly still be nil at that point, even though it then goes on to test it against #isResolved and #isRejected right afterward. > > I find the impedance mismatch between values and Promises of values confusing, too, -- afterall, I thought the purpose was to be able to use them interchangeably. Equally, I wish 'error' were always some kind of Exception, and would signal it when #value (wait) was sent instead of exposing Promises by forcing clients to ask, #isRejected? #isResolved? > > - Chris > > > > On Sun, Jun 21, 2020 at 12:46 PM Jakob Reschke <[hidden email]> wrote: >> >> I am trying to come up with a nice solution for this failing test. >> >> Am So., 21. Juni 2020 um 19:27 Uhr schrieb <[hidden email]>: >> > >> > A new version of KernelTests was added to project The Inbox: >> > http://source.squeak.org/inbox/KernelTests-jr.383.mcz >> > >> > ==================== Summary ==================== >> > >> > Name: KernelTests-jr.383 >> > Author: jr >> > Time: 21 June 2020, 7:27:47.560506 pm >> > UUID: ecddb3cb-25c9-6c43-a879-59e527a3a9ee >> > Ancestors: KernelTests-tonyg.382 >> > >> > Test whether Notifications reject the promises of future sends >> > >> > In my opinion, they should not. >> > >> > =============== Diff against KernelTests-tonyg.382 =============== >> > >> > Item was added: >> > + ----- Method: PromiseTest>>testFutureResolutionWithNotification (in category 'tests - future') ----- >> > + testFutureResolutionWithNotification >> > + | p | >> > + p := [Notification signal: 'should not reject'. 3 + 4] future value. >> > + self assert: (self waitUntil: [p isResolved or: [p isRejected]] orCycleCount: 100). >> > + self assert: p isResolved. >> > + self assert: 7 equals: p value.! >> > >> > >> > |
Hi Jakob, Sorry for the delayed reply. So many things right now! I was sometimes confused about this too: future itself does not return Ah! Sorry, you're right. So "value" there is just to satisfy the compiler. I guess the #future magic-trick kinda worked against its own "readability" in that specific case, huh?.. :) (#yourself would be better form there, IMO).
#future also breaks the formatter, which I use constantly and, since it has to assume the receiver might be a Morph, has to go through Project current addDeferredUIMessage:, MUCH slower than basic process #fork'ing, which is all I needed. I don't think you ever could use Promises and values interchangeably. It essentially renders its #value message completely and utterly useless, since you can't discern between whether nil was the result or simply still running, without checking one of the state messages but... why? That's why I overrode Promise>>#value to: ^ self wait Interchangeability via #value also means that chaining "just works". My code can always simply resolveWith: a value or a Promise of a value, and if its a Promise, the existing code chains it for me, without ever having to bring the #then: API into my code.
Yes. And your program's error-handling can worry about its own errors only, not having to account for BrokenPromise.. Also it would look I think you can get the original stack from the error that's signaled. I'd welcome a better multiprocess debugging tool, for sure, it can be difficult to figure out when things don't go as expected. Also note that you can reject a promise with something else than an Yes, but I don't use it that way, because I prefer the interchangeability. Honestly, it brings all kinds of case-logic into the clients code. You would not be able to distinguish that Current Promise doesn't even have a way to ask #isPending, you always have to check both terminal states (#isResolved, #isRejected) usually with a "not" in there. Ugh. Maybe we could have a I did subclass Promise in the GraphQL framework to do just that and, it worked beautifully. Took me quite a bit of mind-wrangling to finally arrive at this simplicity, all the extra stuff I didn't need really took me on a mental ride around the barnyard just to figure out I didn't need it. :) During that ride, I did notice a minor inefficiency in Promise>>#wait -- for already-resolved Promises, it could simply check isResolved before going through the Semaphore. Best, Chris |
Free forum by Nabble | Edit this page |