[Cuis-dev] Methods that return multiple values

rabbit rabbit at callistohouse.org
Sat Jun 24 08:11:13 PDT 2023


One more update. Demonstrating multi return value handling, multi return handler sending, and promise pipelining. Here is a new version <68> of PromisesLocal, attached. I cleaned up #<<*.

For exploring in Squeak, as Cuis is breaking on #becomeForward:, doIt the following and goto the RefsTest.

> Installer ss project: 'Cryptography'; install: 'PromisesLocal'.

I added a promise chaining test, such that another handler is sent to the resulting promise of the second union handler, accepting all three return objects. This union 2 handler returns g, which the chain handler expects. So here is promise pipelining, where messages sent to a promise have a message sent to it. So this is 6 promises.

> RefsTest>>#testMultiReturnsFromNearPromiseWithPromiseChaining
>
> | selectCount pair promise chainFlag promiseChain union1Flag union2Flag |
> union1Flag := false.
> union2Flag := false.
> chainFlag := false.
> selectCount := 0.
> promise := (pair := self promiseResolverPair) key xgcd: 9.
> promise <<* [:each | selectCount := selectCount + 1. Transcript cr; show: 'each = ', each].
> promise <<* [:g :s :t | union1Flag := true. Transcript cr; show: 'Union 1: g = ', g, '; s = ', s, '; t = ', t. g].
> promiseChain := promise <<* [:g :s :t | union2Flag := true. Transcript cr; show: 'Union 2: g = ', g, '; s = ', s, '; t = ', t. g].
> promiseChain <<* [:g | chainFlag := true. Transcript cr; show: 'Chain of Union 2: g = ', g. g].
> pair value resolve: 21.
> (Delay forMilliseconds: 333) wait.
> self assert: (selectCount == 3).
> self assert: (union1Flag).
> self assert: (union2Flag).
> self assert: (chainFlag).

Here is the Transcript output:

> each = 3
> each = 7
> each = 3
> Union 1: g = 3; s = 7; t = 3
> Union 2: g = 3; s = 7; t = 3
> Chain of Union 2: g = 3

--
❤️‍🔥🐰

On 6/24/23 10:30, rabbit via Cuis-dev wrote:

> My apologies for the undue traffic. I feel the need to clarify what is happening. The promise local is set to the result of the eventual send of #xgcd: to the promise of the initial pair. So we have 2 promises. When the resolver of the initial pair is resolved, it resolves the initial pair promise, which sends the #xgcd: message and thus resolves the 2nd promise to cause the multiResponseHanldlers to be sent in turn. Each send of #<<* also returns a promise, so we actually have 3 momentarily, 3 times, each before the 3rd gets GCed. Before the 4th & 5th? I dunno. Perhaps we have 5 Promises momentarily.
>
> rabbit
>
> On 6/24/23 10:18, rabbit via Cuis-dev wrote:
>
>> I forgot to mention, to resolve the promise <pair value resolve: 21>.
>>
>> But I'm getting this #becomeForward: error on promise resolution, in Cuis. It's a temporary issue.
>>
>>> promise := ((pair := self promiseResolverPair) key xgcd: 9).
>>>
>>> promise <<* [:each | selectCount := selectCount + 1. Transcript cr; show: 'each = ', each];
>>> promise <<* [:g :s :t | firstUnionFlag := true. Transcript cr; show: 'gcd = 'g'].
>>> promise <<* [:g :s :t | secondUnionFlag := true. Transcript cr; show: 'g = ', g, '; s = ', s, '; t = ', t.].
>>>
>>> pair value resolve: 21.
>>
>> On 6/24/23 10:03, rabbit via Cuis-dev wrote:
>>
>>> Hi.
>>>
>>> An interesting facet of eventual sending with promises is the bonus of sending multiple result handlers to a single eventual send result promise. So multiple result handlers, or whatever best describes, for whatever that is worth.
>>>
>>> Here are 3 return handlers,
>>>
>>>> promise := ((pair := self promiseResolverPair) key xgcd: 9).
>>>
>>> the first for each argument
>>>
>>>> promise <<* [:each | selectCount := selectCount + 1. Transcript cr; show: 'each = ', each];
>>>
>>> and the last 2 for all three arguments.
>>>
>>>> promise <<* [:g :s :t | firstUnionFlag := true. Transcript cr; show: 'gcd = 'g'].
>>>>
>>>> promise <<* [:g :s :t | secondUnionFlag := true. Transcript cr; show: 'g = ', g, '; s = ', s, '; t = ', t.].
>>>
>>> --
>>> ❤️‍🔥🐰
>>>
>>> On 6/23/23 16:15, Luciano Notarfrancesco wrote:
>>>
>>>> Right, good point! Fortunately, messages with multiple return values are not very common in my system, I just have a few examples, and I’ll rewrite them with Juan’s suggestion, and I’ll keep your point in mind.
>>>>
>>>> On Fri, 23 Jun 2023 at 21:33 Nicolas Cellier <nicolas.cellier.aka.nice at gmail.com> wrote:
>>>>
>>>>> Hi all,
>>>>> please note that the keyword message did eliminate the positional semantics found in almost every other language.
>>>>> With multiple return arguments, as well as multiple arguments block workaround, we are back to a kind of positional semantic.
>>>>> Of course, there is still some name used as block args [:g :s :t | ...] but nothing would tell that [:t :g :s | ...] is a mistake.
>>>>> A hint on order can be given by the keyword used to pass block argument, like qr: [:quo :rem | ] or whatever, but when there is more than 2, naming is getting difficult.
>>>>> Nicolas
>>>>>
>>>>> Le ven. 23 juin 2023 à 21:00, rabbit via Cuis-dev <cuis-dev at lists.cuis.st> a écrit :
>>>>>
>>>>>> I just realized I have been working in squeak. I updated PromisesLocal-rabbt.66.mcz and am attaching it. I am getting the error below when I 'browse code' or 'install Mionticello package'. I do not know. How can I load Monticello packages? Any way to get the Installer available in cuis? Install Monticello config maps?
>>>>>>
>>>>>> I appreciate you,
>>>>>> rabbit
>>>>>>
>>>>>> On 6/22/23 16:21, rabbit via Cuis-dev wrote:
>>>>>>
>>>>>>> Hey! I got inspired by your proposal, Luciano, and here's my solution in eventual promises. Code can be loaded as:
>>>>>>>
>>>>>>>> Installer ss project: 'Cryptography'; install: 'ProCrypto.release.3'.
>>>>>>>
>>>>>>> I added a test method
>>>>>>>
>>>>>>>> RefsTest>>#testMultiReturns
>>>>>>>>
>>>>>>>> | selectCount unionFlag pair |
>>>>>>>> unionFlag := false.
>>>>>>>> selectCount := 0.
>>>>>>>> ((pair := self promiseResolverPair) key xgcd: 9)
>>>>>>>> <<* [:each | selectCount := selectCount + 1. Transcript cr; show: 'each = ', each];
>>>>>>>> <<* [:g :s :t | unionFlag := true. Transcript cr; show: 'g = ', g, '; s = ', s, '; t = ', t. g + s + t].
>>>>>>>> pair value resolve: 21.
>>>>>>>> (Delay forMilliseconds: 333) wait.
>>>>>>>> self assert: (selectCount == 3).
>>>>>>>> self assert: (unionFlag).
>>>>>>>
>>>>>>> This calls new multireturn #xgcd: method
>>>>>>>
>>>>>>>> xgcd: anInteger
>>>>>>>> " 21 xgcd: 9"
>>>>>>>> | g s t |
>>>>>>>> g := self gcd: anInteger.
>>>>>>>> s := self / g.
>>>>>>>> t := anInteger / g.
>>>>>>>> ^ { g. s. t}
>>>>>>>
>>>>>>> And in the test method there are eventual sends (#<<*) to the promise of the #xgcd: send once the reciever promise is resolved to 21. This implementation detects cardinality of the continuation block and sends #whenResolved: appropriately, for future evaluation.
>>>>>>>
>>>>>>>> Object>>#<<* continuation
>>>>>>>>
>>>>>>>> (continuation numArgs == 1)
>>>>>>>> ifTrue: [^ self whenResolved: [:result | result do: [:each | continuation value: each]]].
>>>>>>>> ^ self whenResolved: [:result | continuation valueWithArguments: result].
>>>>>>>
>>>>>>> One can see a 1 cardinality schedules the continuation to evaluate with each result, while a continuation with cardinality 2 or more will be scheduled to receive all results. #valueWithArguments:
>>>>>>>
>>>>>>> Here are 2 return handlers,
>>>>>>>
>>>>>>>> promise := ((pair := self promiseResolverPair) key xgcd: 9).
>>>>>>>
>>>>>>> the first for each argument
>>>>>>>
>>>>>>>> promise <<* [:each | selectCount := selectCount + 1. Transcript cr; show: 'each = ', each];
>>>>>>>
>>>>>>> and the second for all three arguments.
>>>>>>>
>>>>>>>> promise <<* [:g :s :t | unionFlag := true. Transcript cr; show: 'g = ', g, '; s = ', s, '; t = ', t. g + s + t].
>>>>>>>
>>>>>>> --
>>>>>>> ❤️‍🔥🐰
>>>>>>>
>>>>>>> On 6/21/23 09:44, Luciano Notarfrancesco via Cuis-dev wrote:
>>>>>>>
>>>>>>>> Interesting, thanks for sharing!
>>>>>>>> Actually I think it’s the same thing I did, my implementation calls BlockClosure>>#valueWithPossibleArgs: and takes only as many arguments as needed by the block but the array can be bigger than that (it should be named #valueWithPossibleArguments: tho, abbreviations are ugly).
>>>>>>>>
>>>>>>>> On Wed, 21 Jun 2023 at 15:37 Christian Haider via Cuis-dev <cuis-dev at lists.cuis.st> wrote:
>>>>>>>>
>>>>>>>>> I added something similar to my Values package (VW and ports).
>>>>>>>>>
>>>>>>>>> The source is
>>>>>>>>>
>>>>>>>>> SequenceableCollection>>asArgumentsIn: aBlock
>>>>>>>>>
>>>>>>>>> "Evaluate aBlock with the receiver's elements as parameters.
>>>>>>>>>
>>>>>>>>> aBlock takes its arguments from the receiver.
>>>>>>>>>
>>>>>>>>> 'ok'
>>>>>>>>>
>>>>>>>>> #(1 2 3) asArgumentsIn: [:a :b :c | a + b + c]
>>>>>>>>>
>>>>>>>>> #(1 2 3) asArgumentsIn: [:a :b | a + b]
>>>>>>>>>
>>>>>>>>> #(1 2 3) asArgumentsIn: [:a | a]
>>>>>>>>>
>>>>>>>>> #(1 2 3) asArgumentsIn: [42]
>>>>>>>>>
>>>>>>>>> 'not ok'
>>>>>>>>>
>>>>>>>>> #(1 2 3) asArgumentsIn: [:a :b :c :d | a + b + c + d]
>>>>>>>>>
>>>>>>>>> "
>>>>>>>>>
>>>>>>>>> ^aBlock cullWithArguments: self asArray
>>>>>>>>>
>>>>>>>>> The difference is that it takes a list of any size and picks out the first items and binds them to the variables.
>>>>>>>>>
>>>>>>>>> I use it often for CSV processing like
>>>>>>>>>
>>>>>>>>> (line tokensBasedOn: $;) asArgumentsIn: [:first :second :y | … ].
>>>>>>>>>
>>>>>>>>> I am just a bit unhappy with the name – it is too long. It reads ok though.
>>>>>>>>>
>>>>>>>>> The pipe character is an interesting idea. I have to think about it.
>>>>>>>>>
>>>>>>>>> I am use it for a while now and I am very happy with it.
>>>>>>>>>
>>>>>>>>> Happy hacking,
>>>>>>>>>
>>>>>>>>> Christian
>>>>>>>>>
>>>>>>>>> Von: Cuis-dev <cuis-dev-bounces at lists.cuis.st> Im Auftrag von Luciano Notarfrancesco via Cuis-dev
>>>>>>>>> Gesendet: Mittwoch, 21. Juni 2023 15:13
>>>>>>>>> An: Discussion of Cuis Smalltalk <cuis-dev at lists.cuis.st>
>>>>>>>>> Cc: Luciano Notarfrancesco <luchiano at gmail.com>
>>>>>>>>> Betreff: [Cuis-dev] Methods that return multiple values
>>>>>>>>>
>>>>>>>>> Smalltalk doesn’t have a convention for methods returning multiple values, and I’m not aware of any implementation.
>>>>>>>>>
>>>>>>>>> An example of such thing is the extended gcd: ‘a xgcd: b’ returns g, s, t where g is the gcd, and as + bt = g. Writing methods that return multiple values is easy with the curly brackets syntax, Integer>>#xgcd: ends with something like
>>>>>>>>>
>>>>>>>>> ^ {g. s. t}
>>>>>>>>>
>>>>>>>>> But using sending messages that return multiple values is kind of annoying, I end up doing something like:
>>>>>>>>>
>>>>>>>>> xgcd := a xgcd: b.
>>>>>>>>>
>>>>>>>>> g := xgcd at: 1.
>>>>>>>>>
>>>>>>>>> s := xgcd at: 2.
>>>>>>>>>
>>>>>>>>> t := xgcd at: 3
>>>>>>>>>
>>>>>>>>> Some years ago I thought about using blocks for this, but I never tried it. Today I just did a little experiment implementing anArray | aBlock as ‘^ aBlock valueWithPossibleArgs: self’ and I can do:
>>>>>>>>>
>>>>>>>>> (a xgcd: b) | [:g :s :t| … ]
>>>>>>>>>
>>>>>>>>> This is seems quite nice already, I guess I’ll start using it and see how it feels. But the point of this mail is not to show a solution, but to ask if anyone have thought about this or if they know any nicer solutions. Any ideas?
>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> Cuis-dev mailing list
>>>>>>>>> Cuis-dev at lists.cuis.st
>>>>>>>>> https://lists.cuis.st/mailman/listinfo/cuis-dev
>>>>>>
>>>>>> --
>>>>>> ❤️‍🔥🐰
>>>>>>
>>>>>> --
>>>>>> Cuis-dev mailing list
>>>>>> Cuis-dev at lists.cuis.st
>>>>>> https://lists.cuis.st/mailman/listinfo/cuis-dev
>>>
>>> --
>>> ❤️‍🔥🐰
>>
>> --
>> ❤️‍🔥🐰
>
> --
> ❤️‍🔥🐰

--
❤️‍🔥🐰
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.cuis.st/mailman/archives/cuis-dev/attachments/20230624/943d5e78/attachment-0001.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: BhpEokqJiowsCL1P.jpeg
Type: image/jpeg
Size: 170169 bytes
Desc: not available
URL: <http://lists.cuis.st/mailman/archives/cuis-dev/attachments/20230624/943d5e78/attachment-0001.jpeg>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: PromisesLocal.68.st
Type: application/vnd.sailingtracker.track
Size: 56883 bytes
Desc: not available
URL: <http://lists.cuis.st/mailman/archives/cuis-dev/attachments/20230624/943d5e78/attachment-0001.bin>


More information about the Cuis-dev mailing list