String >> #numArgs

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

String >> #numArgs

Christoph Thiede

Hi all!


Consider the following:


foo := [:a :b | a raisedTo: b].
bar := #raisedTo:.

foo value: 2 value: 3. "5"
bar value: 2 value: 3. "5"

foo numArgs. "2"
bar numArgs. "1"

How can I overcome this inconsistency?

In situations where a symbol acts as "evaluable", quaking like a block, it is confusing to get these different results

Does there already exist a unified protocol? Or am I doing something wrong in general when I need this?


Best,

Christoph



Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

Tobias Pape

> On 14.12.2019, at 21:11, Thiede, Christoph <[hidden email]> wrote:
>
> Hi all!
>
> Consider the following:
>
> foo := [:a :b | a raisedTo: b].
> bar := #raisedTo:.
>
> foo value: 2 value: 3. "5"
> bar value: 2 value: 3. "5"
>
> foo numArgs. "2"
> bar numArgs. "1"
>
> How can I overcome this inconsistency?

There is none.
Symbols are not code.

I see no point beyond apparent convenience to unify them further....
-t

> In situations where a symbol acts as "evaluable", quaking like a block, it is confusing to get these different results
> Does there already exist a unified protocol? Or am I doing something wrong in general when I need this?
>
> Best,
> Christoph



Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

timrowledge


> On 2019-12-15, at 3:24 AM, Tobias Pape <[hidden email]> wrote:
>
>
>> On 14.12.2019, at 21:11, Thiede, Christoph <[hidden email]> wrote:
>>
>> Hi all!
>>
>> Consider the following:
>>
>> foo := [:a :b | a raisedTo: b].
>> bar := #raisedTo:.
>>
>> foo value: 2 value: 3. "5"
>> bar value: 2 value: 3. "5"
>>
>> foo numArgs. "2"
>> bar numArgs. "1"
>>
>> How can I overcome this inconsistency?
>
> There is none.
> Symbols are not code.
>
> I see no point beyond apparent convenience to unify them further....

+1. A Symbol simply isn't nice to use that way. This isn't Lisp, y'know.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Strange OpCodes: PRM: PRint Money



Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

Eliot Miranda-2
In reply to this post by Christoph Thiede
Hi Christoph,

On Sun, Dec 15, 2019 at 3:14 AM Thiede, Christoph <[hidden email]> wrote:

Hi all!


Consider the following:


foo := [:a :b | a raisedTo: b].
bar := #raisedTo:.

foo value: 2 value: 3. "5"
bar value: 2 value: 3. "5"

foo numArgs. "2"
bar numArgs. "1"

How can I overcome this inconsistency?


By defining a new selector, e.g. evaluableNumArgs which either adds 1 to a Selector's numArgs (I think the right thing), or subtracts one from a block's numArgs (nooooooo).  However, it would be wise to keep this as your private extension and not commit it to trunk as per the objections that Tobias and Tim have raised.

In situations where a symbol acts as "evaluable", quaking like a block, it is confusing to get these different results

Does there already exist a unified protocol? Or am I doing something wrong in general when I need this?


Can you give some examples of where you find you are needing to do this?
 

Best,

Christoph




--
_,,,^..^,,,_
best, Eliot


Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

Squeak - Dev mailing list
In reply to this post by Christoph Thiede
Besides 2 raisedTo: 3  should answer 8, not 5.

/————————————————————/
For encrypted mail use [hidden email]
Get a free account at ProtonMail.com
Web: www.objectnets.net and www.objectnets.org

On Dec 15, 2019, at 03:15, Thiede, Christoph <[hidden email]> wrote:



Hi all!


Consider the following:


foo := [:a :b | a raisedTo: b].
bar := #raisedTo:.

foo value: 2 value: 3. "5"
bar value: 2 value: 3. "5"

foo numArgs. "2"
bar numArgs. "1"

How can I overcome this inconsistency?

In situations where a symbol acts as "evaluable", quaking like a block, it is confusing to get these different results

Does there already exist a unified protocol? Or am I doing something wrong in general when I need this?


Best,

Christoph




Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

marcel.taeumel
bar := #raisedTo:.
> bar value: 2 value: 3. 

Woah! This works? When and why is this useful? Does anybody recall the prime example? :-) #inject:into:?

I mean, "bar numArgs" works as expected and so does "foo numArgs". That #value:value: on Symbol is surprising for me here.

Best,
Marcel

Am 16.12.2019 04:57:56 schrieb John Pfersich via Squeak-dev <[hidden email]>:

Besides 2 raisedTo: 3  should answer 8, not 5.

/————————————————————/
For encrypted mail use [hidden email]
Get a free account at ProtonMail.com
Web: www.objectnets.net and www.objectnets.org

On Dec 15, 2019, at 03:15, Thiede, Christoph <[hidden email]> wrote:



Hi all!


Consider the following:


foo := [:a :b | a raisedTo: b].
bar := #raisedTo:.

foo value: 2 value: 3. "5"
bar value: 2 value: 3. "5"

foo numArgs. "2"
bar numArgs. "1"

How can I overcome this inconsistency?

In situations where a symbol acts as "evaluable", quaking like a block, it is confusing to get these different results

Does there already exist a unified protocol? Or am I doing something wrong in general when I need this?


Best,

Christoph




Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

Christoph Thiede

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 :-)"

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.

@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].
But the following does not work:
#(1 2 3) collectX: #+.

Maybe I would rather need something like #value:cull: for an optional second argument?

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]].
... }

Then I could say at least:
#(1 2 3) collectX: #+ asBlock.
And also:
#raisedTo:modulo: asBlock value: 2 value: 3 value: 4. "does not understand"

Best,
Christoph

Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Montag, 16. Dezember 2019 09:22:42
An: John Pfersich via Squeak-dev
Betreff: Re: [squeak-dev] String >> #numArgs
 
bar := #raisedTo:.
> bar value: 2 value: 3. 

Woah! This works? When and why is this useful? Does anybody recall the prime example? :-) #inject:into:?

I mean, "bar numArgs" works as expected and so does "foo numArgs". That #value:value: on Symbol is surprising for me here.

Best,
Marcel

Am 16.12.2019 04:57:56 schrieb John Pfersich via Squeak-dev <[hidden email]>:

Besides 2 raisedTo: 3  should answer 8, not 5.

/————————————————————/
For encrypted mail use [hidden email]
Get a free account at ProtonMail.com
Web: www.objectnets.net and www.objectnets.org

On Dec 15, 2019, at 03:15, Thiede, Christoph <[hidden email]> wrote:



Hi all!


Consider the following:


foo := [:a :b | a raisedTo: b].
bar := #raisedTo:.

foo value: 2 value: 3. "5"
bar value: 2 value: 3. "5"

foo numArgs. "2"
bar numArgs. "1"

How can I overcome this inconsistency?

In situations where a symbol acts as "evaluable", quaking like a block, it is confusing to get these different results

Does there already exist a unified protocol? Or am I doing something wrong in general when I need this?


Best,

Christoph




Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

Tobias Pape
Hi

> On 16.12.2019, at 13:18, Thiede, Christoph <[hidden email]> 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

Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

Christoph Thiede

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?

A symbol should not answer to an #value: … #value:value:...

Levente should know it, he implemented Symbol >> #value:value:.
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?

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?

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Tobias Pape <[hidden email]>
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 <[hidden email]> 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



Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

Levente Uzonyi
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.

>
> > 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.

>
> > 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.

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


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 <[hidden email]> im Auftrag von Tobias Pape <[hidden email]>
> 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 <[hidden email]> 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
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

Nicolas Cellier


Le lun. 16 déc. 2019 à 16:54, Levente Uzonyi <[hidden email]> 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

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.

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 :)


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 <[hidden email]> im Auftrag von Tobias Pape <[hidden email]>
> 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 <[hidden email]> 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
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

Levente Uzonyi
On Mon, 16 Dec 2019, Nicolas Cellier wrote:

>
>
> Le lun. 16 déc. 2019 à 16:54, Levente Uzonyi <[hidden email]> 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 <[hidden email]> im Auftrag von Tobias Pape <[hidden email]>
>       > 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 <[hidden email]> 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
>       >
>       >
>       >
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: String >> #numArgs

Stéphane Rollandin
>> The alternative would be to send a message for transforming a Symbol into an evaluable thing...

Just for the fun of it (and as a pointer to some not-well-known work):

        #+ asLambda value: 2 value: 3
       

This comes from
http://www.zogotounga.net/comp/squeak/functionaltalk.htm

Stef