II'm new at Smalltalk and I have a few questions. """Why smalltalk has no operator for piping ?""""
obj collect: [ :x | …] | filter: [:x | …] | select: [:x | …] ?
but has the `;` (cascade operator)?
I don’t care to send messages to the reciever of the previous message, because I return self when no return value is important; for instance ….
point x: 10 | y: 20 | z: 30
x returns self, y returns self, z returns self -> which is the updated point.
works exactly as:
point x:10; y:20 ; z:30.
But you lose PIPES !!!!! Can somebody elaborate on this ?
There might be cases where I'm returning a value and I wish to discard it and then send again to the previous receiver ... but I really don't like to loose the pipe for that ... since I can do that returning self.
With Smalltalk ... we've got a homoiconic language and the system written in it self, a consistent language in everything, even the if- then-else that everybody else still don't get right; we've got the most wonderful programming experience of all, even better that Ruby/ Python etc ...
but why on earth we have the "cascade operator" instead of the PIPE ????? What I'm I missing ?
I need the `|` to avoid parenthesis, and weird hacks ...
Fabio
On Fri, Aug 24, 2007 at 10:57:48PM +0200, Fabio Filasieno wrote:
II'm new at Smalltalk and I have a few questions. """Why smalltalk has no operator for piping ?""""
obj collect: [ :x | ?] | filter: [:x | ?] | select: [:x | ?] ?
I don't understand what a pipe is or should do, other than save a few parentheses. I am guessing you want the above to be equivilant to (( obj collect: [ :x | ?] ) filter: [:x | ?] ) select: [:x | ?]
(by the way, filter: is not a Smalltalk message)
One reason there is no pipe operator is that '|' is a valid message and has many implementors in the image already.
More importantly, the above chain of messages is bordering on the comprehensable and should probably be split up into more statements or methods.
but has the `;` (cascade operator)?
I don?t care to send messages to the reciever of the previous message, because I return self when no return value is important; for instance ?.
point x: 10 | y: 20 | z: 30
x returns self, y returns self, z returns self -> which is the updated point.
works exactly as:
point x:10; y:20 ; z:30.
But you lose PIPES !!!!! Can somebody elaborate on this ?
There might be cases where I'm returning a value and I wish to discard it and then send again to the previous receiver ... but I really don't like to loose the pipe for that ... since I can do that returning self.
You raise a fair point; most messages retrurn self anyway, and they are the ones that ; is usually used with anyway, so it is a bit redundant.
With Smalltalk ... we've got a homoiconic language and the system written in it self, a consistent language in everything, even the if- then-else that everybody else still don't get right; we've got the most wonderful programming experience of all, even better that Ruby/ Python etc ...
but why on earth we have the "cascade operator" instead of the PIPE ????? What I'm I missing ?
I need the `|` to avoid parenthesis, and weird hacks ...
Like what?
What do you mean by pipes? Are you refering to some kind of streaming like in bash where output can be generated from one process and input to another with finite memory? If so, I am not sure that is possible in general; objects are not always serializable.
What kind of use case do you have in mind?
I need the `|` to avoid parenthesis, and weird hacks ...
Like what?
What do you mean by pipes? Are you refering to some kind of streaming like in bash where output can be generated from one process and input to another with finite memory? If so, I am not sure that is possible in general; objects are not always serializable.
What kind of use case do you have in mind?
let me elaborate:
a message1 | message2
is equal to:
a message1 message2
and:
a message1: param | message2: param2
is equeal to:
(a message1: param) message2: param2
and as you can see writing code with so-called 'pipes' makes code much cleaner
-- Matthew Fulmer -- http://mtfulmer.wordpress.com/ Help improve Squeak Documentation: http://wiki.squeak.org/squeak/808
On Fri, 24 Aug 2007 19:39:17 -0700, Igor Stasenko siguctua@gmail.com wrote:
a message1 | message2
is equal to:
a message1 message2
and:
a message1: param | message2: param2
is equeal to:
(a message1: param) message2: param2
and as you can see writing code with so-called 'pipes' makes code much cleaner
Huh. I don't see it. The first one...okay, I guess, maybe.
But the parens in the ST in the second one are almost a graphic representation of the object being created, to which the message2 is being passed.
On Sat, Aug 25, 2007 at 05:39:17AM +0300, Igor Stasenko wrote:
I need the `|` to avoid parenthesis, and weird hacks ...
Like what?
What do you mean by pipes? Are you refering to some kind of streaming like in bash where output can be generated from one process and input to another with finite memory? If so, I am not sure that is possible in general; objects are not always serializable.
What kind of use case do you have in mind?
let me elaborate:
a message1 | message2
is equal to:
a message1 message2
and:
a message1: param | message2: param2
is equeal to:
(a message1: param) message2: param2
and as you can see writing code with so-called 'pipes' makes code much cleaner
Those two examples are exactly the same length in characters, so I don't see what metric qualifies one as much cleaner than the other.
Parenthesis are not very common in squeak, and this would be less common as parenthesis. Parentheses are more general too; you could not do this with pipes:
dict at: (self keyFor: anItem) put: 3
I am really missing what the point of this is
On 25/08/07, Matthew Fulmer tapplek@gmail.com wrote:
On Sat, Aug 25, 2007 at 05:39:17AM +0300, Igor Stasenko wrote:
I need the `|` to avoid parenthesis, and weird hacks ...
Like what?
What do you mean by pipes? Are you refering to some kind of streaming like in bash where output can be generated from one process and input to another with finite memory? If so, I am not sure that is possible in general; objects are not always serializable.
What kind of use case do you have in mind?
let me elaborate:
a message1 | message2
is equal to:
a message1 message2
and:
a message1: param | message2: param2
is equeal to:
(a message1: param) message2: param2
and as you can see writing code with so-called 'pipes' makes code much cleaner
Those two examples are exactly the same length in characters, so I don't see what metric qualifies one as much cleaner than the other.
try write following using pipes and see the difference. The point is not a number of characters , but how easy you can read the code:
(((a message1: param1) message2:param2) ifTrue: [ b ] ifFalse: [ c ]) message3:param3
Parenthesis are not very common in squeak, and this would be less common as parenthesis. Parentheses are more general too; you could not do this with pipes:
dict at: (self keyFor: anItem) put: 3
no, you can't do this with pipes. pipes is like a cascade, but with same message passing semantics like regular continuation and allow you to mix binary or keyword messages in one continuation without using parenthesis:
a + b | mixWith: c | squared + 5
I am really missing what the point of this is
-- Matthew Fulmer -- http://mtfulmer.wordpress.com/ Help improve Squeak Documentation: http://wiki.squeak.org/squeak/808
On 8/25/07, Igor Stasenko siguctua@gmail.com wrote:
no, you can't do this with pipes. pipes is like a cascade, but with same message passing semantics like regular continuation and allow you to mix binary or keyword messages in one continuation without using parenthesis:
a + b | mixWith: c | squared + 5
Well, pipe is probably a bad name for this. It is a statement delimiter symbol like . or ; but it is short hand for allowing one to send a message to the result of the previous expression.
On Aug 25, 2007, at 8:23 PM, Jason Johnson wrote:
On 8/25/07, Igor Stasenko siguctua@gmail.com wrote:
no, you can't do this with pipes. pipes is like a cascade, but with same message passing semantics like regular continuation and allow you to mix binary or keyword messages in one continuation without using parenthesis:
a + b | mixWith: c | squared + 5
Well, pipe is probably a bad name for this. It is a statement delimiter symbol like . or ; but it is short hand for allowing one to send a message to the result of the previous expression.
You are totally right ... since it was close to the Unix pipe I just called ... .pipe ... But it would be a delimiter exactly as you say.
Fabio
On Aug 25, 2007, at 4:39 AM, Igor Stasenko wrote:
let me elaborate:
a message1 | message2
is equal to:
a message1 message2
and:
a message1: param | message2: param2
is equeal to:
(a message1: param) message2: param2
and as you can see writing code with so-called 'pipes' makes code much cleaner
Igor expressed what I'm after....
On Aug 25, 2007, at 4:43 AM, Blake wrote:
But the parens in the ST in the second one are almost a graphic representation of the object being created, to which the message2 is being passed.
Look down ... the graphic representation is a poor one because It's too costly: too may parenthesis.
On Aug 25, 2007, at 4:51 AM, Matthew Fulmer wrote:
Those two examples are exactly the same length in characters, so I don't see what metric qualifies one as much cleaner than the other.
check this out ...
(((((((((((obj message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param
there is this thing > ((((((((((((
now look the cleaner pipe .... obj message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param
now some mix and match ...
obj | send | left: a right:b | send | send | send | left: a right:b | message
The pipe is needed to support a pipe&filter style of programming. That perfectly works with Smalltalk syntax, and truly opens up a better way of doing functional transformations.
On Aug 25, 2007, at 4:51 AM, Matthew Fulmer wrote:
Parenthesis are not very common in squeak, and this would be less common as parenthesis. Parentheses are more general too; you could not do this with pipes:
dict at: (self keyFor: anItem) put: 3
I am really missing what the point of this is
It's less general but since functional transformations are a very powerful idiom (Haskells monads, Haskells Arrows, the Unix pipe, ... just to name a few) a little help from the language would be nice. It' not a parentesis vs pipe problem, it's a pipe vs cascade. Why ST has the cascade ? Can someone explain it me ... Why need a special operator for that ? I don't care about cascading... but maybe there is something I'm missing.
Parentheses are more general too
Use Lisp than. Everything is a pair... and you'll have as many general parenthesis you want :-p .... just joking ... I see you point but ... my argument is not pipe vs parenthesis, but pipe vs cascade, because I want the the pipe&filter style of programming
I state that:
>>> cascade is useless <<<<<<<<<<<<
because it's redundant :
Example :
point x:10; y:10; z:10.
Your could write in fact. (((point x:10) y:10) z:10) since x:10 returns self anyway, y:10 returns self anyway and z:10 returns self anyway.
but thanks to the ";" (cascade) you can write ... point x:10; y:10; z:10.
Then I say ... ok so the designer wants to remove those ugly parenthesis ... why not do a better job by using a more general operator ??
I can do the cascade stuff ...
point x:10 | y:10 | z:10
and .. magic ... I can do better functional transformations, pipe&filters style.
obj message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param
With the pipe I removed parenthesis from much wider selection of cases, I enable a cleaner way of writing functional transformations, and therefore it's a better design.
Fabio's proposal:
make the ";" cascade operator send the message not the the previous receiver but to the object returned by the previous message send.
Fabio Filasieno
On Aug 25, 2007, at 6:17 AM, Fabio Filasieno wrote:
obj message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param
now some mix and match ...
obj | send | left: a right:b | send | send | send | left: a right:b | message
The pipe is needed to support a pipe&filter style of programming. That perfectly works with Smalltalk syntax, and truly opens up a better way of doing functional transformations.
This all seems very hypothetical. In what problem domain would you end up writing code like this? I've never written such code myself, and I haven't seen anyone else write such code either. Maybe that's just because people shy away from it because of all of the parentheses, but I can't accept that without a real code example to support it.
Josh
On Aug 25, 2007, at 7:05 PM, Joshua Gargus wrote:
On Aug 25, 2007, at 6:17 AM, Fabio Filasieno wrote:
obj message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param | message1: param | message2:param
now some mix and match ...
obj | send | left: a right:b | send | send | send | left: a right:b | message
The pipe is needed to support a pipe&filter style of programming. That perfectly works with Smalltalk syntax, and truly opens up a better way of doing functional transformations.
This all seems very hypothetical. In what problem domain would you end up writing code like this? I've never written such code myself, and I haven't seen anyone else write such code either. Maybe that's just because people shy away from it because of all of the parentheses, but I can't accept that without a real code example to support it.
Josh
a sequence of filter, map, fold, zip, ... it's extreamly common in any case you have to do quite some collection manipulation.
I don't know about other people, but I'll tell where I use it...
Selection, Projection, Cartesian Product, Union, Difference, Intersection are operators that I use a lot in any data selection/manipulation context.
I use them as an "embedded SQL" on objective data. You have persistent data in a file. You load the object graph. You query, delete, add, select data according to what results you get from Selection, Projection, Cartesian Product, Union, Difference, Intersection.
Making easy to combine operators is not hypothetical ... the domain is the most popular: data selection and manipulation. The first thing that comes to my mind is websites.
Example:
db getBlogposts | filter: [ :blogPost | blogPost data < (today - 7 days)] | filter: [ :blogPost | db coolPosts includes: item ) | collectMails | do: [ :mail | "Happy to announce ..."]
This kind of coding is extremely common ... the whole Python language is built around maps, tuples, list ... an in Ocaml and Haskell too ... the list/map libraries with their zip, foldr, foldl, etx ... It's easy to think how you would use these kind functions with the PIPE.
On 8/25/07, Fabio Filasieno fabio.filasieno@gmail.com wrote:
a sequence of filter, map, fold, zip, ... it's extreamly common in any case you have to do quite some collection manipulation.
This kind of programming does come up often from folks who have a lot of functional programming experience (myself included). Sometimes you have an ordered collection and you need to do a series of transformations on it to get the set you want. And it does get annoying to have to keep piling on the parenthesis.
But one reason others aren't sympathetic to this is probably because of the nature of Smalltalk. Most of the methods don't have that many statements. If you use the language for a while you will probably also find that once you end up with several statements grouped in the parenthesis that you could in fact use a part of those transformations/reductions/filtering somewhere else, and therefor should break some of it out to other methods.
On Aug 25, 2007, at 2:57 PM, Fabio Filasieno wrote:
db getBlogposts | filter: [ :blogPost | blogPost data < (today - 7 days)] | filter: [ :blogPost | db coolPosts includes: item )] | collectMails | do: [ :mail | "Happy to announce ..."]
Thanks for the example. This is a very clean looking bit of code. However, as others have noted, it's not fair to conclude that you've won the argument after comparing it to: ((((db getBlogposts) filter: [ :blogPost | blogPost data < (today - 7 days)]) filter: [ :blogPost | db coolPosts includes: item )]) collectMails ) do: [ :mail | "Happy to announce ..."]
You can format the above so that it is very close to your example: ((((db getBlogposts ) filter: [ :blogPost | blogPost data < (today - 7 days)] ) filter: [ :blogPost | db coolPosts includes: item )] ) collectMails ) do: [ :mail | "Happy to announce ..."]
In my opinion, your code looks nicer for two reasons. You don't need the initial parentheses, and '|' looks nicer to me than ')'. However, it's been established that '|' is not available, so I tried out your example with some of the other proposals from the thread.
db getBlogposts ` filter: [ :blogPost | blogPost data < (today - 7 days)] ` filter: [ :blogPost | db coolPosts includes: item )] ` collectMails ` do: [ :mail | "Happy to announce ..."]
db getBlogposts $ filter: [ :blogPost | blogPost data < (today - 7 days)] $ filter: [ :blogPost | db coolPosts includes: item )] $ collectMails $ do: [ :mail | "Happy to announce ..."]
db getBlogposts ;; filter: [ :blogPost | blogPost data < (today - 7 days)] ;; filter: [ :blogPost | db coolPosts includes: item )] ;; collectMails ;; do: [ :mail | "Happy to announce ..."]
The backtick is perhaps the cleanest-looking alternative, and it doesn't have any conflicting associations with other Smalltalk syntax.
I don't like '$' very much... it looks very "heavy", and it looks a lot like a character literal even though it has nothing to do with them. I'm not sure how much we benefit by looking familiar to Haskell programmers. <ducks and covers...> Probably not as much as by adopting Java syntax to be familiar to the curly-brace crowd ;-) .
My favorite is ';;'. Most importantly, it shares both visual and semantic similarity with the cascade operator, since both operators affect the receiver of the subsequent message. Also, IMHO it looks quite clean.
Josh
On Aug 26, 2007, at 12:54 PM, Joshua Gargus wrote:
On Aug 25, 2007, at 2:57 PM, Fabio Filasieno wrote:
db getBlogposts | filter: [ :blogPost | blogPost data < (today - 7 days)] | filter: [ :blogPost | db coolPosts includes: item )] | collectMails | do: [ :mail | "Happy to announce ..."]
Thanks for the example. This is a very clean looking bit of code. However, as others have noted, it's not fair to conclude that you've won the argument after comparing it to: ((((db getBlogposts) filter: [ :blogPost | blogPost data < (today - 7 days)]) filter: [ :blogPost | db coolPosts includes: item )]) collectMails ) do: [ :mail | "Happy to announce ..."]
You can format the above so that it is very close to your example: ((((db getBlogposts ) filter: [ :blogPost | blogPost data < (today - 7 days)] ) filter: [ :blogPost | db coolPosts includes: item )] ) collectMails ) do: [ :mail | "Happy to announce ..."]
In my opinion, your code looks nicer for two reasons. You don't need the initial parentheses, and '|' looks nicer to me than ')'. However, it's been established that '|' is not available, so I tried out your example with some of the other proposals from the thread.
db getBlogposts ` filter: [ :blogPost | blogPost data < (today - 7 days)] ` filter: [ :blogPost | db coolPosts includes: item )] ` collectMails ` do: [ :mail | "Happy to announce ..."]
db getBlogposts $ filter: [ :blogPost | blogPost data < (today - 7 days)] $ filter: [ :blogPost | db coolPosts includes: item )] $ collectMails $ do: [ :mail | "Happy to announce ..."]
db getBlogposts ;; filter: [ :blogPost | blogPost data < (today - 7 days)] ;; filter: [ :blogPost | db coolPosts includes: item )] ;; collectMails ;; do: [ :mail | "Happy to announce ..."]
The backtick is perhaps the cleanest-looking alternative, and it doesn't have any conflicting associations with other Smalltalk syntax.
I don't like '$' very much... it looks very "heavy", and it looks a lot like a character literal even though it has nothing to do with them. I'm not sure how much we benefit by looking familiar to Haskell programmers. <ducks and covers...> Probably not as much as by adopting Java syntax to be familiar to the curly-brace crowd ;-) .
My favorite is ';;'. Most importantly, it shares both visual and semantic similarity with the cascade operator, since both operators affect the receiver of the subsequent message. Also, IMHO it looks quite clean.
Oops, I forgot about '!'...
db getBlogPosts ! filter: [ :blogPost | blogPost data < (today - 7 days)] ! filter: [ :blogPost | db coolPosts includes: item )] ! collectMails ! do: [ :mail | "Happy to announce ..."]
This falls in to the same category as the backtick, although personally I don't like it as much visually.
I still like ';;' best.
Josh
Josh
Joshua Gargus wrote:
Oops, I forgot about '!'...
db getBlogPosts ! filter: [ :blogPost | blogPost data < (today - 7 days)] ! filter: [ :blogPost | db coolPosts includes: item )] ! collectMails ! do: [ :mail | "Happy to announce ..."]
This falls in to the same category as the backtick, although personally I don't like it as much visually.
I still like ';;' best.
Josh
How about "_|"?
| today | today := Timepoint today. db getBlogPosts _| filter: [ :blogPost | blogPost date < (today - 7 days)] _| filter: [ :blogPost | db coolPosts includes: item )] _| collectMails _| do: [ :mail | "Happy to announce ..."]
On 8/25/07, Fabio Filasieno fabio.filasieno@gmail.com wrote:
check this out ...
(((((((((((obj message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param
This is a bit contrived. If you ever did find the need to do this it should look like:
(((((((((((obj message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param
Code smell to be sure, but you pile what you don't like on one line and say you can't read it while formatting what you do like and saying "see!".
The pipe is needed to support a pipe&filter style of programming. That perfectly works with Smalltalk syntax, and truly opens up a better way of doing functional transformations.
pipe&filter style of programming? Most people call this "functional programming" and it doesn't require a pipe operator to work. Such an operator can make certain code cleaner to be sure, but it doesn't "open up a better way of doing functional transforms" by any means. Just saves you one character per function call.
>>>> cascade is useless <<<<<<<<<<<<
because it's redundant :
Example :
point x:10; y:10; z:10.
Your could write in fact. (((point x:10) y:10) z:10) since x:10 returns self anyway, y:10 returns self anyway and z:10 returns self anyway.
In this case yes, one could use parenthesis, the cascade operator or a new statement delimiter (which you call "pipe"). But sometimes you wish to send a bunch of messages to an object that may not return self.
With the pipe I removed parenthesis from much wider selection of cases, I enable a cleaner way of writing functional transformations, and therefore it's a better design.
You don't have a conflict between cascade and your "pipe" unless you want to use the ; symbol. Cascade is a special message that causes the compiler to put in some op codes for duplicating the cascaded object on the stack. What you want is just the current behavior without needing the parenthesis to disambiguate. I don't think it would be too hard to add a new symbol that has the compiler treat it's left side the same as it would if the statement was in parenthesis.
On Aug 25, 2007, at 8:40 PM, Jason Johnson wrote:
This is a bit contrived. If you ever did find the need to do this it should look like:
(((((((((((obj message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param
Code smell to be sure, but you pile what you don't like on one line and say you can't read it while formatting what you do like and saying "see!".
To me there is not much difference between ...
(((((((((((obj message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) message2:param
and
(((((((((((obj message1: param) message2:param) message1: param) message2:param) message1: param) message2:param) message1: param) essage2:param) message1: param) message2:param) message1: param) message2:param
1. They both suck ... 2. While writing and thinking ... I have always to stop and think ... "ok, now where do I need to put the bloody parenthesis"
The pipe is needed to support a pipe&filter style of programming. That perfectly works with Smalltalk syntax, and truly opens up a better way of doing functional transformations.
pipe&filter style of programming? Most people call this "functional programming" and it doesn't require a pipe operator to work. Such an operator can make certain code cleaner to be sure, but it doesn't "open up a better way of doing functional transforms" by any means. Just saves you one character per function call.
I call it too functional programming ... but there are many definitions of it ... for somebody functional programming is no side-effects and nothing else, for others higher order functions is ok ...
since I didn't want to get into that, I just said pipe&filters ... i thought it was alright ... thanks for this clarification ... now everybody now we are talking about ...
Such an operator can make certain code cleaner to be sure, but it doesn't "open up a better way of doing functional transforms" by any means. Just saves you one character per function call.
Not only it can make " certain code cleaner" , but It changes the way you think, because it's like the full stop in regular English ...
Simple train of thought example ...
----
here I'm Thinking<<
here I'm doing ----
ok so what do I have to do ... first get I get some objects<<<
storage getSomeObject >> than .... (and I put my pipe symbol ) ... put a PIPE << |
Already here it cool ...I don't have to ... think .. "Gee I need to go back and put a parenthesis .."
........
storage getSomeCollection >> than .... aaaaand PIPE << | filter: [:obj | obj creationDate >10] >>>>> some more mumble mumble .... aaaaaand PIPE<< | collect: [:obj | obj.name] >>>> some more mumble mumble .... and PIPE <<
.... her some more mumble mumble .... because I'm not sure what
to do BUT I do know that I've got some names.... I never need to go back and put parenthesis .. and this does not break my thoughts ....<<<<
......
storage getSomeCollection >>> than .... (and I put my pipe symbol ) <<< | filter: [:obj | obj creationDate >10] >>>>> than .... (and I put my pipe symbol )... and PIPE << | collect: [:obj | obj.name] >>>>than .... (and I put my pipe symbol )... and PIPE<< | filter: [:name | name isFemale ] | filter: [:name | beutifullGirlsDB contains:name ] | do: [:name | mailTo: (beutifullGirlsDB getWithName:name) message: "What are you doing tonight ?" ]
mmm ... I could do a few things better ... filter and collect
less ... so I'll do another pass .... At this point I might write a Unit test and later I'll make it faster ... etc etc ....<<<
---------------------------------
To me it's like a full stop in english. Maybe you are different. But I think that most people like full stops when a sentence ends.
The pipe is the full stop ... ready to start the next manipulation ...
Fabio Filasieno
On 8/25/07, Igor Stasenko siguctua@gmail.com wrote:
let me elaborate:
a message1 | message2
is equal to:
a message1 message2
and:
a message1: param | message2: param2
is equeal to:
(a message1: param) message2: param2
and as you can see writing code with so-called 'pipes' makes code much cleaner
It is true that it can be nice to avoid using so many parenthesis. This is why Haskell made the $ operator which does precisely the same thing.
But this issue has nothing to do with the very useful and needed cascade operator.
I think the simplicity of operator precedence more than makes up for the extra parentheses IMHO. Though the cascade operator is a special case, in a way!
-----Original Message----- From: squeak-dev-bounces@lists.squeakfoundation.org [mailto:squeak-dev-bounces@lists.squeakfoundation.org] On Behalf Of Jason Johnson Sent: 25 August 2007 7:18 pm To: The general-purpose Squeak developers list Subject: Re: pipe
On 8/25/07, Igor Stasenko siguctua@gmail.com wrote:
let me elaborate:
a message1 | message2
is equal to:
a message1 message2
and:
a message1: param | message2: param2
is equeal to:
(a message1: param) message2: param2
and as you can see writing code with so-called 'pipes' makes code much cleaner
It is true that it can be nice to avoid using so many parenthesis. This is why Haskell made the $ operator which does precisely the same thing.
But this issue has nothing to do with the very useful and needed cascade operator.
Hi Fabio,
welcome to the list! I guess what you would express with pipes would be written in Smalltalk like this?
(((obj collect: [ :x | …]) filter: [:x | …] ) select: [:x | …])
And with a pipe operator you would save a few parntheses here?
With unary message sends, the syntax gives you a kind of piping for free, just chain the messages:
'abc' reverse first isVowel
With keyword messages the syntax is in favor of multiple keyword message selectors:
'abc' detect: [:a | a isVowel] ifAbsent: ['']
is the message #detect:ifAbsent: and not a cascade of two messages #detect and #ifAbsent.
Smalltalk syntax was deliberately designed to be simple, based on a few mechanisms. By the way, your pipe idea is reflected in some methods in the collection hierarchy:
Collection>>select:thenDo: Collection>>select:thenCollect:
Cheers
Matthias
On 8/24/07, Fabio Filasieno fabio.filasieno@gmail.com wrote:
II'm new at Smalltalk and I have a few questions. """Why smalltalk has no operator for piping ?""""
obj collect: [ :x | …] | filter: [:x | …] | select: [:x | …] ?
but has the `;` (cascade operator)?
I don't care to send messages to the reciever of the previous message, because I return self when no return value is important; for instance ….
point x: 10 | y: 20 | z: 30
x returns self, y returns self, z returns self -> which is the updated point.
works exactly as:
point x:10; y:20 ; z:30.
But you lose PIPES !!!!! Can somebody elaborate on this ?
There might be cases where I'm returning a value and I wish to discard it and then send again to the previous receiver ... but I really don't like to loose the pipe for that ... since I can do that returning self.
With Smalltalk ... we've got a homoiconic language and the system written in it self, a consistent language in everything, even the if- then-else that everybody else still don't get right; we've got the most wonderful programming experience of all, even better that Ruby/ Python etc ...
but why on earth we have the "cascade operator" instead of the PIPE ????? What I'm I missing ?
I need the `|` to avoid parenthesis, and weird hacks ...
Fabio
On Aug 25, 2007, at 10:43 AM, Matthias Berth wrote:
Hi Fabio,
welcome to the list!
Thanks :-). Love to be here.
(((obj collect: [ :x | …]) filter: [:x | …] ) select: [:x | …])
And with a pipe operator you would save a few parntheses here?
Yes but there is a problem with that. to quote you:
" (((point x:10) y:20) z:10)
And with a cascade operator you would save a few parntheses here? " I'm not on the `pipe vs parenthesis problem`, but `pipe vs cascade`.
With unary message sends, the syntax gives you a kind of piping for free, just chain the messages:
'abc' reverse first isVowel
Ahaaaaa.... :-D ... got you ... so you DO like functional transformations... Then why not ...
'abc' reverse first isVowel asChars | filter: [ :char | char isCaps] | fold: [: xxx | ... ] with: 0 | toList | collect: [:number | number + 1]
(messages are invented)
Smalltalk syntax was deliberately designed to be simple, based on a few mechanisms. By the way, your pipe idea is reflected in some methods in the collection hierarchy:
Collection>>select:thenDo: Collection>>select:thenCollect:
This is very very ugly ... It's like hardcoding. Dump all those do:thenDo:thenDo. It's the ugliest part of smalltalk. I saw posts on the internet with the same argument and I don't buy it.
I't the hardcoded sequence that sucks.
The pipe is what made Unix powerful ... imagine a Unix system where you need to hardcode compositions of processes.
example: Do your prefer to have ...
ls grep lsThenGrep
or
lr grep ls ... | grep ...
??
I want flexible compositions !
Just dump the cascade for the pipe ... and you have just added to smalltalk part of the power of the unix pipe&filter.
It's better because the cascade is redundant.
(((point x:10) y:10) z:10) ... see it's redundant (it's might be in 95% of cases, I don't have stats).
Can anyone do a stat on Smalltalk messages to see how many times the cascade operator is used and the previous message returns self ? If this number is very high(say 90-95%) then this would prove that the cascade operator is useless, as parenthesis could be used; then we would need a way to reduce parenthesis and an interesting solution would be the Pipe operator.
we could get cascading
point x:10 | y:10 | z:10...
and get for free pipes & filters
x select: [:item | ... ] | collect: [:item | ...] | doThis doThat | fold: [:item | ...] with:0 | mailItto: 'xxxx' | log: [ :result | .... ]
Cascading is redundant if the previous message returns self as parenthesis could be used. In MY case, ALL cascade operations are redundant as I use them for setting properties during object initialization, therefore returning self. Since cascading is redundant (in my case), therefore we can remove the cascade operator. No we need a better way reduce parenthesis in as many cases as possible. The pipe happens to work quite well as I can both do the initialization of objects AND it supports the flexible pipe & filters style.
proposal ...
make the ";" cascade operator send the message not the the previous receiver but to the object returned by the previous message send.
Fabio
On 8/25/07, Fabio Filasieno fabio.filasieno@gmail.com wrote:
Hi, welcome to the list and Smalltalk. :)
I'm not on the `pipe vs parenthesis problem`, but `pipe vs cascade`.
There is no "pipe vs cascade" problem. In Smalltalk the language is based sending messages to objects. If you want to send a message to the result of the last send then you can just do it. If you wish instead to send more messages to a previous object you can use the cascade operator.
So if you want "pipe" behavior you have it already. The only time parenthesis are needed is when there is ambiguity about evaluation order.
Haskell uses the $ operator to reduce the need to have parenthesis in a manner you're suggesting.
Ahaaaaa.... :-D ... got you ... so you DO like functional transformations...
Of course, Smalltalk is full of them.
This is very very ugly ... It's like hardcoding. Dump all those do:thenDo:thenDo. It's the ugliest part of smalltalk. I saw posts on the internet with the same argument and I don't buy it.
These are just convenience methods.
Just dump the cascade for the pipe ... and you have just added to smalltalk part of the power of the unix pipe&filter.
It's better because the cascade is redundant.
In what way?
(((point x:10) y:10) z:10) ... see it's redundant (it's might be in 95% of cases, I don't have stats).
Are you sure you know what the cascade operator is doing? It can't be redundant because there is no other way to do what it's doing without using temp variables.
E.g. if "point x: 10" returns 10 then you can't go:
(point x: 10) y: 10
because the "y: 10" goes to the other 10. You must either use different statements as in:
point x: 10. point y: 10. "..."
Or you can use the cascade operator as in:
point x: 10; y: 10.
So where is the redundancy?
Cascading is redundant if the previous message returns self as parenthesis could be used. In MY case, ALL cascade operations are redundant as I use them for setting properties during object initialization, therefore returning self. Since cascading is redundant (in my case), therefore we can remove the cascade operator.
Lol. *You* don't need cascading so we should break with the Smalltalk 80 spec, break backward compatibility with.... pretty much all Smalltalk code written until now, and have to type out verbose repeat statements over and over since we have no cascade operator anymore? Does that really seem logical/realistic to you?
My suggestion would be to learn the language before redesigning it. ;)
Oh and by the way, you may have heard about the Seaside framework that's gaining popularity right now. The API it uses to create HTML is built around the cascade operator.
On Aug 25, 2007, at 8:11 PM, Jason Johnson wrote:
On 8/25/07, Fabio Filasieno fabio.filasieno@gmail.com wrote:
Hi, welcome to the list and Smalltalk. :)
Thank's Jason ... really happy to be here ....
I'm not on the `pipe vs parenthesis problem`, but `pipe vs cascade`.
There is no "pipe vs cascade" problem. In Smalltalk the language is based sending messages to objects. If you want to send a message to the result of the last send then you can just do it. If you wish instead to send more messages to a previous object you can use the cascade operator.
So if you want "pipe" behavior you have it already. The only time parenthesis are needed is when there is ambiguity about evaluation order.
No you don't ...
This is ugly ... ((( obj collect: [ :x | ....] ) filter: [:x | …] ) select: [:x | …])
This is beautiful ... obj | collect: [ :x | ....] | filter: [:x | …] | select: [:x | …]
Haskell uses the $ operator to reduce the need to have parenthesis in a manner you're suggesting.
Haskell's $ is ugly again ... because you would need to write backwards .... there are better ways (Arrows)
Why on hell I have to read from right to left ....
Ahaaaaa.... :-D ... got you ... so you DO like functional transformations...
Of course, Smalltalk is full of them.
This is very very ugly ... It's like hardcoding. Dump all those do:thenDo:thenDo. It's the ugliest part of smalltalk. I saw posts on the internet with the same argument and I don't buy it.
These are just convenience methods.
Are you suggesting That I need to write convenience methods at every single bind I do ? I know you could do that ... but it's ugly ...
Just dump the cascade for the pipe ... and you have just added to smalltalk part of the power of the unix pipe&filter.
It's better because the cascade is redundant.
In what way?
(((point x:10) y:10) z:10) ... see it's redundant (it's might be in 95% of cases, I don't have stats).
Are you sure you know what the cascade operator is doing? It can't be redundant because there is no other way to do what it's doing without using temp variables.
E.g. if "point x: 10" returns 10 then you can't go:
(point x: 10) y: 10
because the "y: 10" goes to the other 10. You must either use different statements as in:
point x: 10. point y: 10. "..."
Or you can use the cascade operator as in:
point x: 10; y: 10.
So where is the redundancy?
There is quite some redundancy - as I stated before - when the method returns self. In MY case I use the cascade on setter methods.
And for that you dump the ability to compose processes ?? You dump the power of UNIX ?
Cascading is redundant if the previous message returns self as parenthesis could be used. In MY case, ALL cascade operations are redundant as I use them for setting properties during object initialization, therefore returning self. Since cascading is redundant (in my case), therefore we can remove the cascade operator.
Lol. *You* don't need cascading so we should break with the Smalltalk 80 spec, break backward compatibility with.... pretty much all Smalltalk code written until now, and have to type out verbose repeat statements over and over since we have no cascade operator anymore? Does that really seem logical/realistic to you?
Nope ... but I might hack the compiler .... and see if someone likes the new image, since it's smalltalk, and we can change anything ....
My suggestion would be to learn the language before redesigning it. ;)
I'm not redesigning it, I'm currently writing an interpreter for a programming language and it happens that I selected Squeak for that ... I'll probably start hacking Smalltalk a little anyway ... and yes I like breaking Specs ... you never now. Sometimes something nice comes up ... :-)
Fabio
P.S. The `uglys` here are obviously my personal taste.
On 8/26/07, Fabio Filasieno fabio.filasieno@gmail.com wrote:
So if you want "pipe" behavior you have it already. The only time parenthesis are needed is when there is ambiguity about evaluation order.
No you don't ...
This is ugly ... ((( obj collect: [ :x | ....] ) filter: [:x | …] ) select: [:x | …])
This is beautiful ... obj | collect: [ :x | ....] | filter: [:x | …] | select: [:x | …]
Being ugly and not existing are two very different things. You do have it already, you just have to use parenthesis in the cases where it is ambiguous to the compiler. And I don't find the pipe that pretty as I know it as an "or" operator.
Haskell's $ is ugly again ... because you would need to write backwards .... there are better ways (Arrows)
It's not ugly, it's exactly what you have here. The difference is Haskell binds right to left and Smalltalk left to right. Well actually Haskell binds left to right in things like Monads and right to left in statements.
Why on hell I have to read from right to left ....
Because the last function called is the first thing in the statement. This is exactly the opposite of Smalltalk, which is why the application order is reversed.
There is quite some redundancy - as I stated before - when the method returns self. In MY case I use the cascade on setter methods.
I don't think you read what I wrote. :) The nice thing about the cascade operator is that I don't have to worry what the message returns. I know for sure that my message sends go to the original object.
And for that you dump the ability to compose processes ?? You dump the power of UNIX ?
There are more powerful OS'es then UNIX. And you're not talking about the power of unix anyway, you're talking about the power of shell. You could do the exact same thing in Windows if the utilities were written that way (or use cygwin that *is* written that way).
I'm not redesigning it, I'm currently writing an interpreter for a programming language and it happens that I selected Squeak for that ... I'll probably start hacking Smalltalk a little anyway ... and yes I like breaking Specs ... you never now. Sometimes something nice comes up ... :-)
Fabio
Breaking specs is fine when it's needed, but in this case it makes no sense. Just add a new operator that behaves how you want. It shouldn't be that hard.
2007/8/25, Fabio Filasieno fabio.filasieno@gmail.com:
x select: [:item | ... ]
| collect: [:item | ...] | doThis doThat | fold: [:item | ...] with:0 | mailItto: 'xxxx' | log: [ :result | .... ]
In Self language it will be more simpler.
'x select: [:item | ... ] collect: [:item | ...] doThis doThat fold: [:item | ...] With:0 mailItto: 'xxxx' log: [ :result | .... ]
(The first word of message selector must begins with low char. And other words must begin with high char)
squeak-dev@lists.squeakfoundation.org