[squeak-dev] String >> #numArgs

Levente Uzonyi leves at caesar.elte.hu
Mon Dec 16 23:06:32 UTC 2019


On Mon, 16 Dec 2019, Nicolas Cellier wrote:

> 
> 
> Le lun. 16 déc. 2019 à 16:54, Levente Uzonyi <leves at caesar.elte.hu> a écrit :
>       On Mon, 16 Dec 2019, Thiede, Christoph wrote:
>
>       >
>       > Hi Tobias, thanks for your input!
>       >
>       >
>       > > Why not simply
>       >
>       > >         Matrix rows: 5 columns: 6 tabulate: [:x :y | x @ y]
>       > > 
>       > > What's wrong with blocks?
>       >
>       > At least in scripting scenarios, they cost extra redundant characters. Symbol >> #value:value: is a known selector (at least now), so why should I always type all these redundant letters?
>
>       That's right. The sole advantage of having those methods is that they make
>       writing scripts easier. IIRC, I asked here not to commit code into the
>       Trunk using these "tricks". We can always use blocks, which are more
>       legible for most smalltalkers, and they have better performance as well.
> 
> 
> I find evaluable Symbol very convenient and expressive enough.
> (aCollection select: #odd)  , (aCollection collect: #squared) , (aCollection reduce: #+).
> 
> However, it's kind of abusing the purpose of Symbol class.
> We should avoid a Frog class wanting to become bigger than an Ox class
> (never mind, probably a joke for french https://fr.wikipedia.org/wiki/La_Grenouille_qui_se_veut_faire_aussi_grosse_que_le_b%C5%93uf)
> 
> We can cheat as long as we don't get caught, but with numArgs it sounds like we got caught...
> 
>  
>       >
>       > > A symbol should not answer to an #value: … #value:value:...
>       > Levente should know it, he implemented Symbol >> #value:value:.
>
>       The goal with Symbol>>value:value: was to help writing sort blcoks, but
>       now we have SortFunctions for that.
>       The method follows the semantics of Symbol>>value:.
>       Since sorting doesn't require more than two parameters, I didn't even
>       consider adding further value:* variants.
>
>       > However we solve this, I think, at the moment, we have an irritating inconsistency. Imho, Symbol >> #value: is not better than Symbol >> #value:value at all. And I use it every day, so I would vote for
>       increasing the support.
>       > (BalloonMorph allInstancesDo: #abandon, SystemWindow select: #isInWorld, ...)
>       >
>       > > I would then rather use withIndexCollect. It does what it says on the tin.
>       > At the moment, we have #collect: and #withIndexCollect: side by side. However, their implementations are almost equal, and my problem is that every method that I write to access a collection (e. g. collection
>       accessors) can
>       > only support one of them. Why not exploit the fact that [:x :i | x + i] and [:x | x squared] can be distinguished by #numArgs and unify these two accessors?
>
>       #numArgs is not suitable for that if you want to mix blocks with symbols.
>       Creating a new method (e.g. I saw #arity in a VW discussion) would be a
>       much better option.
> 
> 
> Since we got caught, we now require two different meanings indeed,
> the number of arguments expected by the Symbol used as a selector
> the number of arguments expected by the Symbol used as evaluable
> 
> The alternative would be to send a message for transforming a Symbol into an evaluable thing...
> Like for SortFunction we can send (points sorted: #x ascending , #y descending)
> 
> I don't have good ones in mind, maybe something like (aCollection select: #odd asLambda)... Or #asBlock if we really want to compile a Block on the fly (that's an implementation detail IMO)...
> 
>
>       >
>       > > you can use cull:cull: anyways.
>       > Interesting point. This would also simplify the approaches for our recent #to:collect: issue:
>       > (1 to: 5) collectX: [Color random]
>       >
>       > Could we maybe just adapt SequenceableCollection >> #collect: to support 0 - 2 block arguments instead of exactly one? It would be nice to have this in Trunk!
>       >
>       > > > Symbol >> #asBlock
>       > > that's not too bad :D > but there is already perform:withArguments:, so this should be simpler?
>       >
>       > I would be glad to send something to the Inbox. But how do you create a block by reflection, without knowing the number of arguments?
>       > It looks as if there is no public interface yet and I would have to generate the byte codes manually. Or am I overlooking something?
>
>       That would be a cool feature, but it would make debugging a bit harder,
>       because it would be hard to tell where those blocks come from.
>       On the other hand, it could significantly speed up SortFunctions.
> 
> If compiled once and used many, maybe.

It depends on how long the compilation takes and how large the collection 
is. We can easily compile blocks, but that's faily slow:

Symbol >> #asBlock

 	^Compiler evaluate: (self numArgs caseOf: {
 		[ 0 ] -> [ '[ :x | x {1} ]' format: { self } ].
 		[ 1 ] -> [ '[ :x :y | x {1} y ]' format: { self } ].
 		[ 2 ] -> [ '[ :x :y :z | x {1} y {2} z ]' format: self keywords ] })

>
>       For collectX:, you don't need anything like that. Below is a simple and
>       reasonably fast implementation. Note that it still has worse performance
>       than #collect:.
> 
>
>       collectX: aValuable
>               "Evaluate aValuable with each of the receiver's elements and indices as optional arguments. Collect the resulting values into a collection like the receiver. Answer the new collection."
>
>               | index size newCollection |
>               newCollection := self species new: self size.
>               index := 0.
>               size := self size.
>               aValuable numArgs + (aValuable isSymbol ifTrue: [ 1 ] ifFalse: [ 0 ]) caseOf: {
>                       [ 0 ] -> [
>                               [ (index := index + 1) <= size ] whileTrue: [
>                                       newCollection at: index put: aValuable value ] ].
>                       [ 1 ] -> [
>                               [ (index := index + 1) <= size ] whileTrue: [
>                                       newCollection at: index put: (aValuable value: (self at: index)) ] ].
>                       [ 2 ] -> [
>                               [ (index := index + 1) <= size ] whileTrue: [
>                                       newCollection at: index put: (aValuable value: (self at: index) value: index) ] ] }.
>               ^newCollection
> 
> I have always prefered seeing the other way arround, key first - value last like in keyAndValuesDo: rather than doWithIndex:
> even though this does not lend itself to cull:cull:
> And we could also create associations with (collection collectX: #->)
> Otherwise we need <- like in the other thread :)

I've never seen an implementation of collect/map which doesn't keep the 
object at the first argument when another one is added.

Levente

> 
>
>       Levente
>       [1] http://forum.world.st/The-Trunk-Collections-ar-183-mcz-td481126.html
>
>       P.S.: You may find these other discussions about this topic interesting:
>       http://forum.world.st/Symbol-gt-gt-value-value-td1306944.html
>       http://forum.world.st/Symbol-should-implement-cull-cull-td4758139.html
>       http://forum.world.st/Implementing-cull-cull-in-symbol-td4763781.html
>       http://forum.world.st/Symbol-gt-value-td3778525.html
>       http://forum.world.st/Musing-on-Symbol-gt-gt-value-td3367942.html
>
>       >
>       > Best,
>       > Christoph
>       >
>       >________________________________________________________________________________________________________________________________________________________________________________________________________________________________
>       _
>       > Von: Squeak-dev <squeak-dev-bounces at lists.squeakfoundation.org> im Auftrag von Tobias Pape <Das.Linux at gmx.de>
>       > Gesendet: Montag, 16. Dezember 2019 13:41:03
>       > An: The general-purpose Squeak developers list
>       > Betreff: Re: [squeak-dev] String >> #numArgs  
>       > Hi
>       >
>       > > On 16.12.2019, at 13:18, Thiede, Christoph <Christoph.Thiede at student.hpi.uni-potsdam.de> wrote:
>       > >
>       > > Hi all! :-)
>       > >
>       > > > Woah! This works? When and why is this useful? Does anybody recall the prime example? :-) #inject:into:?
>       > >
>       > > Yes, I believe I once used it with #inject:into:, but I don't remember. What prime example are you referring to? :)
>       > > My personal favorite is:
>       > > Matrix rows: 5 columns: 6 tabulate: #@ "for quickly setting up an example matrix :-)"
>       >
>       > Why not simply
>       >         Matrix rows: 5 columns: 6 tabulate: [:x :y | x @ y]
>       >
>       > What's wrong with blocks?
>       >
>       > >
>       > > I find it rather confusing that the following does not work:
>       > > #raisedTo: value: 2 value: 3. "works"
>       > > #raisedTo:modulo: value: 2 value: 3 value: 4. "does not understand"
>       > > This is an inconsistency.
>       >
>       > Yes. The first case should also bail.
>       >
>       > A symbol answering to #perform: is IMHO borderline.
>       > A symbol's answer to #value should be itself.
>       > A symbol should not answer to an #value: … #value:value:...
>       >
>       >
>       > >
>       > > @Eliot:
>       > > > Can you give some examples of where you find you are needing to do this?
>       > >
>       > > Actually, I was trying something like this:
>       > > SequenceableCollection >> #collectX: aBlock
>       > > ^ aBlock numArgs = 2
>       > > ifTrue: [self withIndexCollect: aBlock]
>       > > ifFalse: [self collect: aBlock]].
>       > >
>       > > Now you can say:
>       > > #(1 2 3) collectX: [:x | x squared].
>       > > #(1 2 3) collectX: #squared.
>       > > #(1 2 3) collectX: [:x :i | x + i].
>       >
>       > That's hacky.
>       >
>       > I would then rather use withIndexCollect. It does what it says on the tin.
>       >
>       > > But the following does not work:
>       > > #(1 2 3) collectX: #+.
>       > >
>       > > Maybe I would rather need something like #value:cull: for an optional second argument?
>       > >
>       >
>       > you can use cull:cull: anyways.
>       >
>       > > In general, from the point of scripting convenience, I would love Symbol to support many more protocols of BlockClosure. What about:
>       > > Symbol >> #asBlock
>       > > ^ self numArgs caseOf: {
>       > > [0] -> [[:rcvr | rcvr perform: self]].
>       > > [1] -> [[:rcvr :arg | rcvr perform: self with: arg]].
>       > > [2] -> [[:rcrv :arg1 :arg2 | rcrv perform: self with: arg1 with: arg2]].
>       > > ... }
>       > >
>       > that's not too bad :D
>       > but there is already perform:withArguments:, so this should be simpler?
>       >
>       > Best regards
>       >         -Tobias
>       >
>       >
>       >
> 
> 
>


More information about the Squeak-dev mailing list