Hello,
I find the following elegant:
aCollection inject: 1 into: #*
(and similarly for all binary messages and single-keyword messages)
more so than the normal:
aCollection inject: 1 into: [ :a :b | a * b]
For example like this:
factorial (1 to: self) inject: 1 into: #*
The price for this seems to be simply:
!Symbol methodsFor: 'converting' stamp: 'mn 1/19/2000 19:38'! value: i value: ii ^i perform: self with: ii! !
And similarly for unary messages
aCollection collect: #negated
What do you think?
/Mats Nygren
Mats Nygren nygren@sics.se wrote:
I find the following elegant: aCollection inject: 1 into: #* (and similarly for all binary messages and single-keyword messages) more so than the normal: aCollection inject: 1 into: [ :a :b | a * b]
For example like this:
factorial (1 to: self) inject: 1 into: #*
The price for this seems to be simply: !Symbol methodsFor: 'converting' stamp: 'mn 1/19/2000 19:38'! value: i value: ii ^i perform: self with: ii! !
And similarly for unary messages aCollection collect: #negated
Well, it seems like January 2000 is the time to recapitulate the interesting discussions of the past. This topic has come up a couple of tiems in the past -- I believe it was Ward Cunningham who first thought of endowing symbols with block-like behavior in this manner (back in Tektronix days, I think). Then about a year ago, Marcel Weiher contributed a cool idea for allowing this kind of cascaded functionals that he called "trampolines" (see his msg of 12/13/1998). The idea is that if a trampoline doesn't understand a message, then it turns around and sends it iteratively to all of its elements.
One of the reasons we didn't actually act on these suggestions on the last go-round was that we got distracted by extending Squeak's numeric coercions to handle collections enabling APL-like examples as
#(1 2 3 4 5) * 2 and 2 * #(1 2 3 4 5).
However, we need to revisit this territory to bring it to completion, since neither
#(1 2 3 4 5) negated nor #(1 2 3 4 5) raisedTo: 2
work in the analogously convenient way. Interestingly, Marcel's trick offers a possible generic solution without having to write coercion code into all the numeric operations. It's also a place where you would like to have numeric protocol be a first-class object.
You have to be careful rushing into this kind of thing, partly because of the subtlety aspect ("obfuscation", as Bob Jarvis puts it). Also if you get an error in a trampoline, things may have gone far beyond what the user had in mind -- as, eg, if you misspell a collection selector, and it turns around and tries to send it to one of its elements. I think there is a "sweet spot" here but, as with some of our other pending projects, I think we need some careful review and experimentation before we pop it into the mainline release.
- Dan
- Dan
- Dan
Dan Ingalls wrote:
One of the reasons we didn't actually act on these suggestions on the last go-round was that we got distracted by extending Squeak's numeric coercions to handle collections enabling APL-like examples as
#(1 2 3 4 5) * 2 and 2 * #(1 2 3 4 5).
This seems the appropriate context to consider set operations on collections, such as union and difference:
#(1 2 3 4 5) asSet union: #(5 6) asSet ==>> Set (1 2 3 4 5 6)
#(1 2 3 4 5) asSet intersect: #(5 6) asSet ==>> Set (5)
This would handle removing and detecting duplicates and such, which seems to crop up from time to time.
As a matter of fact, I would interpret + etc. on sets as set algebraic operators:
set1 + set2 equivalent to set1 union: set2
but this might certainly obfuscate.
The semantics of the operation would depend on the class of the receiver (unlike sets, arrays etc. would not remove duplicates on union:).
To me, this seems another obvious candidate for having been previously considered as well.
Also, is there a reason why there can't be a final period in a { } set constructor? This seems inconsistent with periods in blocks and methods. (Not too big a deal.)
ie. {1. 2.} bad; {1. 2} ok
Henrik
Henrik Gedenryd Henrik.Gedenryd@lucs.lu.se wrote:
Also, is there a reason why there can't be a final period in a { } set constructor? This seems inconsistent with periods in blocks and methods. (Not too big a deal.)
ie. {1. 2.} bad; {1. 2} ok
I didn't know this was the case. I'll see what I can do. Anyone else want to fix this before I get to it?
- Dan
[Nice comments about trampolines] Thanks ! (blush)
You have to be careful rushing into this kind of thing, partly
because of the
subtlety aspect ("obfuscation", as Bob Jarvis puts it).
Definitely! The confusion aspect was very much on my mind, and still is.
Also if you get an error in a trampoline, things may have gone far beyond what the user had
in mind -- as, eg,
if you misspell a collection selector, and it turns around and
tries to send it to
one of its elements.
As they say, this turns out not to be the case :-) The trampoline actually always forwards *all* messages sent to it, so trampolines have to be explicitly requested. There is no logic of the type "if the message is not understood, then send it to the elements", because I agree that this has way too much potential for confusion (I am not good at precision syntax, as amply demonstrated by my posts, so I would be the first person confused)
So, the way to multiply an array by 2 using standard trampoline-like messaging would be
#(1 2 3 4 5) collect * 2.
That makes it a bit less readable/convenient than the array processing code currently in the image, but makes for it for a simple additive syntax. In my original Objective-C implementation, that was actually a shorthand for
#(1 2 3 4 5) each collect * 2.
where "each" returns an iterator. This also explains how collection arguments work:
2 collect * #(1 2 3 4 5) each. or even #(1 2 3 4) collect * #(4 5 6 7) each.
I think it looks quite strange when used with numerical operators, so the current specialized/optimized variants probably make sense to keep.
I think there is a "sweet spot" here but, as with some of our other pending projects, I think we need some careful review and
experimentation
before we pop it into the mainline release.
Sure!
Marcel
At 23:29 +0100 2000.01.19, Marcel Weiher wrote:
[Nice comments about trampolines] Thanks ! (blush)
There have been some interesting comments in this thread about trampolines. This is a term that I have heard used, but I'm not sure exactly what it means. I wonder if Marcel or someone else could enlighten me (and others)?
For example, what is going on in
So, the way to multiply an array by 2 using standard trampoline-like messaging would be
#(1 2 3 4 5) collect * 2.
I'm guessing that #collect on an array returns a "trampoline" object with a reference to the specific array, and then this trampoline understands * and implements it by sending collect: [ :each | each * arg ] to that array.
But how is this implemented? Something more clever than one method in the trampoline for each arithmetic operator, I presume!
As another example, on 18:36 -0500 2000.01.19, Stephen Pair wrote:
I found the trampoline concept very useful...Marcel actually modified some asynchronous message code of mine to use his trampoline stuff. The original code was written like:
anObject asend: #message with: arg
...which was kind of cumbersome. With the trampoline, it simply became:
anObject asend message: arg.
...beautiful!
A agree, and I'm guessing that here the "trampoline" returned by (anObject asend) implements message: arg by doing something like
[myObject message: arg] fork
but, once again, I would like to see how this is implemented in a general way.
Incidentally, Marcel, I did look at your DiplomaThesis, and found it very interesting. It's a very nice survey of some things that I knew about and many others that I did not.
squeak-dev@lists.squeakfoundation.org