Richard O'Keefe is making good points against the Squeak implementation
of method #collect:thenDo: in the Pharo-users list: https://lists.pharo.org/pipermail/pharo-users_lists.pharo.org/2019-September/044204.html I vote for the removal of this confusing method. Stef |
Hi
> On 11.09.2019, at 09:42, Stéphane Rollandin <[hidden email]> wrote: > > Richard O'Keefe is making good points against the Squeak implementation of method #collect:thenDo: in the Pharo-users list: > > https://lists.pharo.org/pipermail/pharo-users_lists.pharo.org/2019-September/044204.html I read it. I see his point but I am attempted to disagree. Here's my reasoning: #collect:thenXXX: for me implies that the block of the collect-part has to abide the same constraints as a "pure" collect. That is, if you do 'abc' collect: [:x | x asMorph] and this fails, then 'abc' collect: [:x | x asMorph] thenDo: [:m | m openInHand]. MUST also fail. Collect is bound to the kind of collection it collects over. We have #collect:as: for kind/species-changing collects. So to support Richard's case, one could imagine |o| o := OrderedCollection new. #[3 1 4 1 5 9] collect: [:byte | byte asFloat] as: OrderedCollection thenDo: [:float | o addLast: float]. Transcript print: o; cr. (which makes the thenDo:-part superfluous, but I understand why the original version is that way) or for the other example: #[3 1 4 1 5 9] collect: [:byte | byte odd ifTrue: [byte printString] ifFalse: [byte]] as: Array thenSelect: [:each | each isNumber] > > I vote for the removal of this confusing method. [TL;DR below] The problem the #enumerationPart:thenOtherEnumerationPart: messages try to solve is our (otherwise helpfully) slim syntax. Because (((foo collect: [:x|x barf]) select: [:y|y isKnorz]) inject: Kiffle into: [:clomb :blui | clomb baz: blui]) sum is hard to write, error-prone and not "scriptable"[1]. Marcel Weiher added syntax for this in his Objective-SmallTalk [sic] which would rather read like foo collect: [:x|x barf |> select: [:y|y isKnorz] |> inject: Kiffle into: [:clomb :blui | clomb baz: blui] |> sum or with breaks: foo collect: [:x|x barf |> select: [:y|y isKnorz] |> inject: Kiffle into: [:clomb :blui | clomb baz: blui] |> sum This makes it all very data-flow-y, which can be really great to understand what's going on with one's collections. Personally, I am not too keen on the '|>' symbolism itself, because |> is just another binary operator and would break current syntax. However, this whole thing is close enough to cascades we already got: forc gnarf: [kelp]; klimb; strosn: [:f :zz | zz klept: f]; badummdz. And _If_ we had something like it, I'd prefer it similarly to this. Maybe ;; (while somewhat stupid, its practical in its own right…), or use ! (no syntactical meaning yet, aside the changeset separation, but looks strange). The ` is free but used in RefactoringBrowser's code matching and compiler error messages, so no good candidate either. Other syntactic variant, but even more out-of-place would be colon-doubling to signal "end of keywords": foo collect:: [:x|x barf] select:: [:y|y isKnorz] inject: Kiffle into:: [:clomb :blui | clomb baz: blui]) sum which is just one stop before Self's syntax, which abuses capitalization (watch for the Into): foo collect: [:x|x barf] select: [:y|y isKnorz] inject: Kiffle Into: [:clomb :blui | clomb baz: blui]) sum Btw: With higher order messages, it would also look quite good: (foo collect barf select isKnorz "inject: Kiffle into: [:clomb :blui | clomb baz: blui]" "<-- not sure here, tho" inject Kiffle into baz: blui) sum TL;DR: the ..:then..: messages have an important role in readability, so should we abandon them, we need something else, maybe even richer syntax. Best regards -Tobias [1]: With that, I mean, you can't forward-progam here. Think you star with some collection and inspect it. Then you go back to your do it, #collect: on it and inspect again. But then, to do the same with another, eg, #select:, first you have to put parenthesis around it, to then go to the end again and add your code. Repeat. |
Wow, those syntactical ideas sound really interesting. However, I think is very important to keep the Smalltalk syntax as minimal as possible, as imho this is one big advantage over other common languages like C*. Smalltalk stands out by its use of self-explaining messages instead of non-intuitive keywords. So what would you think about the following?
foo asZöglfrex
collect: [:x|x barf];
select: [:y|y isKnorz]; inject: Kiffle into: [:clomb :blui | clomb baz: blui]); sum (Or even override #yourself instead of using #receiver.)
The relevant implementation of Zöglfrex would be quite easy:
Object subclass: #Zöglfrex
instanceVariableNames: 'receiver'
classVariableNames: ''
poolDictionaries: ''
category: 'Kernel-Objects'
^ receiver := aMessage sendTo: receiver
Best, Christoph Von: Squeak-dev <[hidden email]> im Auftrag von Tobias Pape <[hidden email]>
Gesendet: Mittwoch, 11. September 2019 10:52:39 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] collect:thenDo: woes Hi
> On 11.09.2019, at 09:42, Stéphane Rollandin <[hidden email]> wrote: > > Richard O'Keefe is making good points against the Squeak implementation of method #collect:thenDo: in the Pharo-users list: > > https://lists.pharo.org/pipermail/pharo-users_lists.pharo.org/2019-September/044204.html I read it. I see his point but I am attempted to disagree. Here's my reasoning: #collect:thenXXX: for me implies that the block of the collect-part has to abide the same constraints as a "pure" collect. That is, if you do 'abc' collect: [:x | x asMorph] and this fails, then 'abc' collect: [:x | x asMorph] thenDo: [:m | m openInHand]. MUST also fail. Collect is bound to the kind of collection it collects over. We have #collect:as: for kind/species-changing collects. So to support Richard's case, one could imagine |o| o := OrderedCollection new. #[3 1 4 1 5 9] collect: [:byte | byte asFloat] as: OrderedCollection thenDo: [:float | o addLast: float]. Transcript print: o; cr. (which makes the thenDo:-part superfluous, but I understand why the original version is that way) or for the other example: #[3 1 4 1 5 9] collect: [:byte | byte odd ifTrue: [byte printString] ifFalse: [byte]] as: Array thenSelect: [:each | each isNumber] > > I vote for the removal of this confusing method. [TL;DR below] The problem the #enumerationPart:thenOtherEnumerationPart: messages try to solve is our (otherwise helpfully) slim syntax. Because (((foo collect: [:x|x barf]) select: [:y|y isKnorz]) inject: Kiffle into: [:clomb :blui | clomb baz: blui]) sum is hard to write, error-prone and not "scriptable"[1]. Marcel Weiher added syntax for this in his Objective-SmallTalk [sic] which would rather read like foo collect: [:x|x barf |> select: [:y|y isKnorz] |> inject: Kiffle into: [:clomb :blui | clomb baz: blui] |> sum or with breaks: foo collect: [:x|x barf |> select: [:y|y isKnorz] |> inject: Kiffle into: [:clomb :blui | clomb baz: blui] |> sum This makes it all very data-flow-y, which can be really great to understand what's going on with one's collections. Personally, I am not too keen on the '|>' symbolism itself, because |> is just another binary operator and would break current syntax. However, this whole thing is close enough to cascades we already got: forc gnarf: [kelp]; klimb; strosn: [:f :zz | zz klept: f]; badummdz. And _If_ we had something like it, I'd prefer it similarly to this. Maybe ;; (while somewhat stupid, its practical in its own right…), or use ! (no syntactical meaning yet, aside the changeset separation, but looks strange). The ` is free but used in RefactoringBrowser's code matching and compiler error messages, so no good candidate either. Other syntactic variant, but even more out-of-place would be colon-doubling to signal "end of keywords": foo collect:: [:x|x barf] select:: [:y|y isKnorz] inject: Kiffle into:: [:clomb :blui | clomb baz: blui]) sum which is just one stop before Self's syntax, which abuses capitalization (watch for the Into): foo collect: [:x|x barf] select: [:y|y isKnorz] inject: Kiffle Into: [:clomb :blui | clomb baz: blui]) sum Btw: With higher order messages, it would also look quite good: (foo collect barf select isKnorz "inject: Kiffle into: [:clomb :blui | clomb baz: blui]" "<-- not sure here, tho" inject Kiffle into baz: blui) sum TL;DR: the ..:then..: messages have an important role in readability, so should we abandon them, we need something else, maybe even richer syntax. Best regards -Tobias [1]: With that, I mean, you can't forward-progam here. Think you star with some collection and inspect it. Then you go back to your do it, #collect: on it and inspect again. But then, to do the same with another, eg, #select:, first you have to put parenthesis around it, to then go to the end again and add your code. Repeat.
Carpe Squeak!
|
Guys I don't know where you went, but the rationale for getting rid of
#collect:thenDo: is simply that it is syntactic sugar only (just hiding two parenthesis), and that it makes it at the same time pretty much useless and very much misleading depending on the receiver, as per Richard's discussion. I am not proposing implementing any new thing, just removing that method. Stef |
Hi, all. Let me try to extract the arguments for and against Collection >> #collect:thenDo: (and similar). Pro - works with most kinds of Collection - in the source code, one less pair of parentheses needed - same semantics as (... collect: [...]) do: [:ea | ...] ... (because it reads almost the same) - in the source code, the word "then" supports readability (or reading flow) a bit Contra - the current implementation creates an extra collection (-> easy to change) - no inherent support for #collect:as: to configure the target/intermediate kind (-> maybe irrelevant?) - having all combinations would blow up the interface: do/collect/select/reject/inject ... collect:thenInject:into:thenReject:thenDo: ... (-> big issue) I think that changing the implementation of #collect:thenDo: to not behave like the "expanded version" -- (... collect: [...]) do: [:ea | ...] -- could be quite confusing. Take the following example: #(1 2 3 4) asSet collect: [:num | num even] thenDo: [:isEven | Transcript show: isEven]. I think that this snippet should output "falsetrue" (or "truefalse") but not "falsetruefalsetrue" (or the other combinations). Otherwise it would be sth. like #do:afterCollectEach: ... brrrrr ... Same for a SortedCollection where the sort order depends on (or changes with) the values *after* the collect operation. After reading Richard's discussion, I do still not understand, when (and how) #collect:thenDo: could be misleading. At least not the way Squeak is implementing it at the moment. It is just syntactic sugar for collect;do. We have plenty of that kind of sugar. :-) Best, Marcel
|
In reply to this post by Stéphane Rollandin
On Wed, 11 Sep 2019, Stéphane Rollandin wrote:
> Richard O'Keefe is making good points against the Squeak implementation > of method #collect:thenDo: in the Pharo-users list: I disagree. His idea is that #collect:thenDo: ought to work differently, calling Squeak's implementation "pointless", because it is what its name suggests: #collect: followed by #do:. But there's a small twist: the collected values are returned. Then he brings up the red herring: examples where #collect: does something a naive user would not expect, and where #collect:as: should be used. Then blames it all on #collect:thenDo:. Next, he shows how good those examples work in Pharo, with the new implementation. Even though he forgets to point out that the new implementation has different semantics (when your blocks have side effects) and different return value (because it doesn't create an intermediate collection to return), which can break your code[1]. In the summary, he writes that "[the Squeak definition] does not improve readability. In fact, it REDUCES readability". I think having a fewer parentheses can help with legibility. He also writes "[the Squeak definition] is useless with most collection classes.". I somewhat agree with this. I wouldn't use it in a method, but it sometimes comes handy in a workspace script (just like #in:). Finally, "[the Squeak definition] does nothing to improve performance but just adds overhead.". I suppose no one wanted to improve performance with that method. > > https://lists.pharo.org/pipermail/pharo-users_lists.pharo.org/2019-September/044204.html > > I vote for the removal of this confusing method. I'm not against deprecating it, because every now and then it brings up a similar discussion, but then all other methods of the same kind (*:then*:) must go as well from the collections. Levente [1] http://forum.world.st/Bad-reimplementation-of-Collection-gt-gt-collect-thenDo-in-Pharo-3-0-td4760774.html > > Stef > > |
In reply to this post by Christoph Thiede
I'm glad to hear you say that, Christoph, because I feel the Collection class>>#new:filledWith: proposal falls into the same category as this -- too many issues. As a development community, I think we should consider moving some of these "syntactic sugar" method ideas into a new "Collections-Extensions" package which could be safely unloaded (e.g., no core code would depend on it).
I don't understand this aversion to parentheses. They're the universal symbol of precedence, not only universally readable, but thanks to Squeak's built-in "expression editing" capabilities, enable a more powerful way to edit code than the sugary syntax that typically leave you "editing text" instead of expressions. I generally don't care for added syntactic sugar that serves only to reduce parentheses. It's not worth the API explosion or costs in portability. +1 to deprecate them or move them to an unloadable Collections-Extensions. Best, Chris
|
> I'm glad to hear you say that, Christoph, because I feel the Collection class>>#new:filledWith: proposal falls into the same category as this -- too many issues. Hm, but this does not change any syntactical aspects? There were no reactions on Collections-mt.852 so far - I would be curious what the issues are!
> I don't understand this aversion to parentheses.
Parentheses are not bad at all, but when your code becomes really lispy, I think the parentheses concept has reached its limits. Consider SQL or LINQ which do not indent each line to another level.
I do not find it easy to read this expression:
((((foo
collect: [:x|x barf]) select: [:y|y isKnorz]) inject: Kiffle into: [:clomb :blui | clomb baz: blui]) reduce: [:z :w | z plong: w])
sum Yes, you can use indentations, but the readability is still suboptimal:
((((foo collect: [:x|x barf])
select: [:y|y isKnorz])
inject: Kiffle into: [:clomb :blui | clomb baz: blui])
reduce: [:z :w | z plong: w])
sum Imho brackets are the one concept and pipelines/queries are another one. If you would like to concatenate really many expressions, brackets turn out not to be "sustainable" any longer, as each additional expression costs a new pair of brackets and a new
indentation. So the parenthesis costs two dimensions of space where pipelines only need one, what makes it really hard to insert another dimension into the bracket monster:
((((foo collect: [:x|x barf])
select: [:y|y isKnorz])
inject: Kiffle into: [:clomb :blui | (clomb baz: [:deng | deng ploof: blui in: Roz])
with: blui collect: [:zi :rad | zi pol: rad]])
reduce: [:z :w | z plong: w])
sum (Where is the inner expression??)
In contrast:
foo asZöglfrex
collect: [:x|x barf];
select: [:y|y isKnorz];
inject: Kiffle into: [:clomb :blui | clomb asZöglfrex
baz: [:deng | deng ploof: blui in: Roz];
with: blui collect: [:zi :rad | zi pol: rad]];
reduce: [:z :w | z plong: w];
sum Best,
Christoph
Von: Squeak-dev <[hidden email]> im Auftrag von Chris Muller <[hidden email]>
Gesendet: Donnerstag, 12. September 2019 00:38:46 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] collect:thenDo: woes
I'm glad to hear you say that, Christoph, because I feel the Collection class>>#new:filledWith: proposal falls into the same category as this -- too many issues.
As a development community, I think we should consider moving some of these "syntactic sugar" method ideas into a new "Collections-Extensions" package which could be safely unloaded (e.g., no core code would depend on it).
I don't understand this aversion to parentheses. They're the universal symbol of precedence, not only universally readable, but thanks to Squeak's built-in "expression editing" capabilities, enable a more powerful way to edit code than the sugary syntax
that typically leave you "editing text" instead of expressions.
I generally don't care for added syntactic sugar that serves only to reduce parentheses. It's not worth the API explosion or costs in portability.
+1 to deprecate them or move them to an unloadable Collections-Extensions.
Best,
Chris
Carpe Squeak!
|
Hi Christoph,
What I think has actually reached its limits is passive reading of textual code, instead of active reading of tile-based code like Scratch. By active, I mean using the interface to delineate the nodes in the hierarchy of expressions, as you read and digest the method. As it turns out, there is a particular combination of settings in Squeak (I really need to document them as a set...) that enables fairly rich expression-editing in the standard Squeak editor. It involves a couple of trade-offs with the conventions of other text editors, but the payoff is huge. In this environment the parentheses actually assist the active reading of the code. Everyone knows about selecting expressions by clicking just inside the parentheses. Another is Command+[space]. Still another assist is that the first 8 or so levels of parentheses can each be set to a unique color. One last trick is to place the text cursor in front of a particular closing bracket, to know its corresponding opening, simply press that closing bracket key to highlight its corresponding opening bracket. Just an alternative way to delineate the expression.
Active reading improves the bandwidth of my understanding of code better than passive reading of the code as a stream of formatted text -- even when that stream of code is sweetened in some way to try to hide the precedence. I think a hierarchy of expressions is easier to see and understand than a sequence of outputs piped to inputs. The former mirrors the structure of the language. The latter can only be fully understood by reading the entire sequence. Best, Chris |
Hi Chris,
thanks for the <cmd>Space tip, this is great! (Btw, I would find it quite useful to press this combination again and again to select each time the parent expression up to the root -- similar to shift + blue button on morphs.)
I see your argument of hierarchic expressions, and I would not use something like zöglfrex in common Smalltalk code. However, I would find it irritating to read long SQL queries in a triangle layout, so finally I think it remains a domain dependent question. :)
Best, Christoph Von: Chris Muller <[hidden email]>
Gesendet: Donnerstag, 12. September 2019 05:15:49 An: Thiede, Christoph Cc: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] collect:thenDo: woes Hi Christoph,
What I think has actually reached its limits is passive reading of textual code, instead of
active reading of tile-based code like Scratch. By active, I mean using the interface to delineate the nodes in the hierarchy of expressions, as you read and digest the method.
As it turns out, there is a particular combination of settings in Squeak (I really need to document them as a set...) that enables fairly rich expression-editing in the standard Squeak editor. It involves a couple of trade-offs with the conventions of
other text editors, but the payoff is huge. In this environment the parentheses actually assist the active reading of the code. Everyone knows about selecting expressions by clicking just inside the parentheses. Another is Command+[space]. Still another
assist is that the first 8 or so levels of parentheses can each be set to a unique color. One last trick is to place the text cursor in front of a particular closing bracket, to know its corresponding opening, simply press that closing bracket key to highlight
its corresponding opening bracket. Just an alternative way to delineate the expression.
Active reading improves the bandwidth of my understanding of code better than passive reading of the code as a stream of formatted text -- even when that stream of code is sweetened in some way to try to hide the precedence. I think a hierarchy of expressions
is easier to see and understand than a sequence of outputs piped to inputs. The former mirrors the structure of the language. The latter can only be fully understood by reading the entire sequence.
Best,
Chris
Carpe Squeak!
|
Free forum by Nabble | Edit this page |