Pipe operator

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

Pipe operator

Garth Holland
Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Ben Coman
S. Garth Holland wrote:
Will Pharo ever get the pipe opeator?


/Garth


I don't follow. That word 'pipe' doesn't appear on that page.
cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

stepharo
In reply to this post by Garth Holland
if you are talking about the extension made by nicolas cellier: probably.

Stef

On 8/6/14 00:10, S. Garth Holland wrote:
Will Pharo ever get the pipe opeator?


/Garth



Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

NorbertHartl
In reply to this post by Garth Holland
I think you need to be more specific. The word pipe is misleading and not mentioned in the reddit post. The solution in that post is not an operator but a pattern you can implement easily yourself. To be more monadic like it wouldn't be this like in the example
item safeChain recipient; address; street.
Here you need to make sure all messages are chained (using the ; in smalltalk is actually chaining) on the proxy. If the proxy does not return the requested value but again a proxy with that value it would come closer.

But not sure what you meant.

Norbert
Am 08.06.2014 um 00:10 schrieb "S. Garth Holland" <[hidden email]>:

Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

NorbertHartl
In reply to this post by stepharo
What is that extension?

Norbert

Am 08.06.2014 um 09:27 schrieb stepharo <[hidden email]>:

if you are talking about the extension made by nicolas cellier: probably.

Stef

On 8/6/14 00:10, S. Garth Holland wrote:
Will Pharo ever get the pipe opeator?


/Garth



Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Nicolas Cellier



2014-06-08 12:12 GMT+02:00 Norbert Hartl <[hidden email]>:
What is that extension?

Norbert


I'm curious too :)
I recently proposed to enable caret as a binary selector, maybe there's been a confusion...
I remember a discussion about using ;; as pipe "operator" (it's more syntax sugar than operator), in august 2007 or so.
 
Am 08.06.2014 um 09:27 schrieb stepharo <[hidden email]>:

if you are talking about the extension made by nicolas cellier: probably.

Stef

On 8/6/14 00:10, S. Garth Holland wrote:
Will Pharo ever get the pipe opeator?


/Garth




Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Garth Holland
In reply to this post by Ben Coman
The pipe operator appears in many languages (F#, Haskell, Elixir, Clojure (threading macro)). It's an elegant way of chaining method/function calls in the presence of additional parameters. The reddit example could be written using a pipe operator |>

 #('apple' 'peach' 'banana')
        |> groupedBy: #size
        |> select: [:each | each size even]
        |> values
        |> collect: #asCommaString.

/Garth
Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Ben Coman
Garth Holland wrote:

> The pipe operator appears in many languages (F#, Haskell, Elixir, Clojure
> (threading macro)). It's an elegant way of chaining method/function calls in
> the presence of additional parameters. The reddit example could be written
> using a pipe operator |>
>
>  #('apple' 'peach' 'banana')
> |> groupedBy: #size
> |> select: [:each | each size even]
> |> values
> |> collect: #asCommaString.
>
> /Garth
>
>
>  
Ah... so you mean instead of doing this?

 (((#('apple' 'peach' 'banana')
        groupedBy: #size )
        select: [:each | each size even] )
        values )
        collect: #asCommaString.

Yes. The former does look nice.
cheers -ben

Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Frank Shearar-3
In reply to this post by Garth Holland
Vassili Bykov wrote about the pipe operator in Smalltalk back in 2007:
http://blog.3plus4.org/2007/08/30/message-chains/

frank

On 8 June 2014 18:20, Garth Holland <[hidden email]> wrote:

> The pipe operator appears in many languages (F#, Haskell, Elixir, Clojure
> (threading macro)). It's an elegant way of chaining method/function calls in
> the presence of additional parameters. The reddit example could be written
> using a pipe operator |>
>
>  #('apple' 'peach' 'banana')
>         |> groupedBy: #size
>         |> select: [:each | each size even]
>         |> values
>         |> collect: #asCommaString.
>
> /Garth
>
>
>
>
> --
> View this message in context: http://forum.world.st/Pipe-operator-tp4762106p4762182.html
> Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
>

Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Attila Magyar
Hi,

I wrote that reddit post. I was curious if anyone came up with something to this. Since then I discovered that  Bert Freudenberg created an asPipe method before, that works exactly the same way as mine. And probably asPipe is a better name than chain. The idea of the pipe operator is interesting as well, but I prefer library based solutions.

Attila
Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Garth Holland
In reply to this post by Frank Shearar-3
The pipe operator was discussed extensively on the Squeak list back in 2007.

http://lists.squeakfoundation.org/pipermail/squeak-dev/2007-August/thread.html#119787

I count 161 pipe related messages. Judging from the bug report http://bugs.squeak.org/view.php?id=6649 , the Squeak team were reluctant to make changes to the language.

OTOH, the pipe operator has become almost essential in languages such as F#, Clojure and Elixir.

/Garth
Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

camille teruel
In reply to this post by Garth Holland

On 8 juin 2014, at 19:20, Garth Holland <[hidden email]> wrote:

> The pipe operator appears in many languages (F#, Haskell, Elixir, Clojure
> (threading macro)). It's an elegant way of chaining method/function calls in
> the presence of additional parameters. The reddit example could be written
> using a pipe operator |>
>
> #('apple' 'peach' 'banana')
> |> groupedBy: #size
> |> select: [:each | each size even]
> |> values
> |> collect: #asCommaString.

The pipe operator is implemented as one of the examples of LanguageBox, a tool that permits to scope language extensions (it's a part of Helvetia made by Lucas Renggli).

I once thought that the pipe operator could be useful. But now I notice that the main (if not the only) recurrent use case I found is chaining queries on collections.
If you have another recurrent use case (that is not purely idiomatic :) ) please let me know.

So yes, using the pipe operator make the snippet more readable but still has much inefficient: at each step a new collection is created just to be trashed afterward.
Here it's not a problem but not all collections only have 3 elements ;).

For me it's a sign that the collection libraries lack a simple abstraction: composable filters (that is, iterators) to make complex queries.
We could have collections answering #query that would responds a QueryBuilder object that would understand common enumeration methods, for example:

#('apple' 'peach' 'banana') query
        groupedBy: #size;
        select: [:each | each size even];
        values;
        collect: #asCommaString.

This would return a CollectFilter on a ValuesFilter on a SelectFilter on a GroupedByFilter on the input collection.
The QueryBuilder could then answer a message like #endQuery to compute and return the values obtained by the lastly created filter (embedded in a collection that conforms to the input collection's #species).
Since it's just a composition of filter objects over the input collection, no useless intermediate collection is created: better performances & less stress on the GC.

Of course this idea is acceptable only if they are no other use cases where the pipe operator is a must.

>
> /Garth
>
>
>
>
> --
> View this message in context: http://forum.world.st/Pipe-operator-tp4762106p4762182.html
> Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
>


Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Attila Magyar
Yes, AFIK, the Java 8 stream API works similarly. The query is called stream(), the endQuery is called collect(Collectors.toList())
Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

stepharo
In reply to this post by Nicolas Cellier


Norbert


I'm curious too :)
I recently proposed to enable caret as a binary selector, maybe there's been a confusion...

:)
Yes my brain is overloaded in this moment. I'm doing far too many things at the same time. Not good.

I remember a discussion about using ;; as pipe "operator" (it's more syntax sugar than operator), in august 2007 or so.
 
Am 08.06.2014 um 09:27 schrieb stepharo <[hidden email]>:

if you are talking about the extension made by nicolas cellier: probably.

Stef

On 8/6/14 00:10, S. Garth Holland wrote:
Will Pharo ever get the pipe opeator?


/Garth





Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Denis Kudriashov
In reply to this post by camille teruel
XStream I think is exactly about it


2014-06-08 22:46 GMT+04:00 Camille Teruel <[hidden email]>:

On 8 juin 2014, at 19:20, Garth Holland <[hidden email]> wrote:

> The pipe operator appears in many languages (F#, Haskell, Elixir, Clojure
> (threading macro)). It's an elegant way of chaining method/function calls in
> the presence of additional parameters. The reddit example could be written
> using a pipe operator |>
>
> #('apple' 'peach' 'banana')
>       |> groupedBy: #size
>       |> select: [:each | each size even]
>       |> values
>       |> collect: #asCommaString.

The pipe operator is implemented as one of the examples of LanguageBox, a tool that permits to scope language extensions (it's a part of Helvetia made by Lucas Renggli).

I once thought that the pipe operator could be useful. But now I notice that the main (if not the only) recurrent use case I found is chaining queries on collections.
If you have another recurrent use case (that is not purely idiomatic :) ) please let me know.

So yes, using the pipe operator make the snippet more readable but still has much inefficient: at each step a new collection is created just to be trashed afterward.
Here it's not a problem but not all collections only have 3 elements ;).

For me it's a sign that the collection libraries lack a simple abstraction: composable filters (that is, iterators) to make complex queries.
We could have collections answering #query that would responds a QueryBuilder object that would understand common enumeration methods, for example:

#('apple' 'peach' 'banana') query
        groupedBy: #size;
        select: [:each | each size even];
        values;
        collect: #asCommaString.

This would return a CollectFilter on a ValuesFilter on a SelectFilter on a GroupedByFilter on the input collection.
The QueryBuilder could then answer a message like #endQuery to compute and return the values obtained by the lastly created filter (embedded in a collection that conforms to the input collection's #species).
Since it's just a composition of filter objects over the input collection, no useless intermediate collection is created: better performances & less stress on the GC.

Of course this idea is acceptable only if they are no other use cases where the pipe operator is a must.

>
> /Garth
>
>
>
>
> --
> View this message in context: http://forum.world.st/Pipe-operator-tp4762106p4762182.html
> Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
>



Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Nicolas Cellier
In reply to this post by camille teruel



2014-06-08 20:46 GMT+02:00 Camille Teruel <[hidden email]>:

On 8 juin 2014, at 19:20, Garth Holland <[hidden email]> wrote:

> The pipe operator appears in many languages (F#, Haskell, Elixir, Clojure
> (threading macro)). It's an elegant way of chaining method/function calls in
> the presence of additional parameters. The reddit example could be written
> using a pipe operator |>
>
> #('apple' 'peach' 'banana')
>       |> groupedBy: #size
>       |> select: [:each | each size even]
>       |> values
>       |> collect: #asCommaString.

The pipe operator is implemented as one of the examples of LanguageBox, a tool that permits to scope language extensions (it's a part of Helvetia made by Lucas Renggli).

I once thought that the pipe operator could be useful. But now I notice that the main (if not the only) recurrent use case I found is chaining queries on collections.
If you have another recurrent use case (that is not purely idiomatic :) ) please let me know.

So yes, using the pipe operator make the snippet more readable but still has much inefficient: at each step a new collection is created just to be trashed afterward.
Here it's not a problem but not all collections only have 3 elements ;).


Of course, there are lazy implementations for streams and collections...

For me it's a sign that the collection libraries lack a simple abstraction: composable filters (that is, iterators) to make complex queries.
We could have collections answering #query that would responds a QueryBuilder object that would understand common enumeration methods, for example:

#('apple' 'peach' 'banana') query
        groupedBy: #size;
        select: [:each | each size even];
        values;
        collect: #asCommaString.

This would return a CollectFilter on a ValuesFilter on a SelectFilter on a GroupedByFilter on the input collection.
The QueryBuilder could then answer a message like #endQuery to compute and return the values obtained by the lastly created filter (embedded in a collection that conforms to the input collection's #species).
Since it's just a composition of filter objects over the input collection, no useless intermediate collection is created: better performances & less stress on the GC.

Of course this idea is acceptable only if they are no other use cases where the pipe operator is a must.


Yes, Michael Lucas Smith proposed selecting: collecting: ... in the thread from 97.
At least for Streams that became a real implementation (Xtreams)
 
>
> /Garth
>
>
>
>
> --
> View this message in context: http://forum.world.st/Pipe-operator-tp4762106p4762182.html
> Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
>



Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Randal L. Schwartz
In reply to this post by Garth Holland
>>>>> "Garth" == Garth Holland <[hidden email]> writes:

Garth> The pipe operator appears in many languages (F#, Haskell, Elixir, Clojure
Garth> (threading macro)).

And Perl6.

--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<[hidden email]> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix consulting, Technical writing, Comedy, etc. etc.
Still trying to think of something clever for the fourth line of this .sig

Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

Frank Shearar-3
In reply to this post by camille teruel
On 8 June 2014 19:46, Camille Teruel <[hidden email]> wrote:

>
> On 8 juin 2014, at 19:20, Garth Holland <[hidden email]> wrote:
>
>> The pipe operator appears in many languages (F#, Haskell, Elixir, Clojure
>> (threading macro)). It's an elegant way of chaining method/function calls in
>> the presence of additional parameters. The reddit example could be written
>> using a pipe operator |>
>>
>> #('apple' 'peach' 'banana')
>>       |> groupedBy: #size
>>       |> select: [:each | each size even]
>>       |> values
>>       |> collect: #asCommaString.
>
> The pipe operator is implemented as one of the examples of LanguageBox, a tool that permits to scope language extensions (it's a part of Helvetia made by Lucas Renggli).
>
> I once thought that the pipe operator could be useful. But now I notice that the main (if not the only) recurrent use case I found is chaining queries on collections.
> If you have another recurrent use case (that is not purely idiomatic :) ) please let me know.

The pipe operator is just a variant of the flip combinator:

Object >> >>> aBlock
    "Obviously just a strawman implementation"
    ^ aBlock value: self.

lets you write

    1 >>> [:x  | x + 1] >>> [:x | x * 2] >>> [:x | x + 1]

which is easier to read than

    [:x | x + 1] value: ([:x | x * 2] value: ([:x | x + 1] value: 1))

both because you have less ()s and because it reads left->right. (OK,
easier for people whose scripts are left->right.)

It's so potent an idiom in F# not only because you can remove a bunch
of ()s but also because F# automatically curries all functions. It's
this - handling keyword selectors - where the problem comes in for
Smalltalk. As Vassili Bykov mentions in his post.

> So yes, using the pipe operator make the snippet more readable but still has much inefficient: at each step a new collection is created just to be trashed afterward.
> Here it's not a problem but not all collections only have 3 elements ;).
>
> For me it's a sign that the collection libraries lack a simple abstraction: composable filters (that is, iterators) to make complex queries.
> We could have collections answering #query that would responds a QueryBuilder object that would understand common enumeration methods, for example:
>
> #('apple' 'peach' 'banana') query
>         groupedBy: #size;
>         select: [:each | each size even];
>         values;
>         collect: #asCommaString.
>
> This would return a CollectFilter on a ValuesFilter on a SelectFilter on a GroupedByFilter on the input collection.
> The QueryBuilder could then answer a message like #endQuery to compute and return the values obtained by the lastly created filter (embedded in a collection that conforms to the input collection's #species).

The key part of this example is the use of the Builder pattern to let
you use cascades. The equivalent in Xtreams would be as lazy, as
performant, but require lots of ()s:

(something like:)

(((#('apple 'peach' banana') groupingBy: #size)
    selecting: [:each | each size even])
        rest) asCommaString

(Your #values is Xtreams' #rest. You could also pull off less than the
entire stream with #read:.)

> Since it's just a composition of filter objects over the input collection, no useless intermediate collection is created: better performances & less stress on the GC.

Also, using these lazy streams also lets you both unfold and fold
without creating intermediate collections:
http://www.lshift.net/blog/2012/04/30/hylomorphisms-through-lazy-streams

frank

> Of course this idea is acceptable only if they are no other use cases where the pipe operator is a must.
>
>>
>> /Garth
>>
>>
>>
>>
>> --
>> View this message in context: http://forum.world.st/Pipe-operator-tp4762106p4762182.html
>> Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
>>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Pipe operator

HidaKenji
In reply to this post by Garth Holland
※I'm not good English. Sorry.
That Operator is the same when implemented in a Collection.
So I tried to make pipe as collection.

https://github.com/devid-rudesheim/rudesheim-base/tree/develop

|
        select
        collect
|

select :=
[ :block |
        RHPipeMessage
                send: #value:
                to:
                [ :streams |
                        streams stdin
                                do:
                                [ :each |
                                        (
                                                block
                                                        value: each
                                        )
                                                ifTrue:
                                                [
                                                        streams stdout
                                                                nextPut: each.
                                                ].
                                ]
                ].
].

collect :=
[ :block |
        RHPipeMessage
                send: #value:
                to:
                [ :streams |
                        streams stdin
                                do:
                                [ :each |
                                        streams stdout
                                                nextPut:
                                                (
                                                        block
                                                                value: each
                                                ).
                                ]
                ].
].

(
        RHPipe
                withPipeCommands:
                {
                        collect
                                value:
                                [ :each |
                                        1 + each
                                ].
                        select
                                value:
                                [ :each |
                                        5 < each
                                ].
                        collect
                                value:
                                [ :each |
                                        each printString.
                                ].
                }
)
                writeStreamDo:
                [ :stream |
                        stream
                                nextPutAll:
                                (
                                        0
                                                to: 10
                                ).
                ]
                readStreamDo:
                [ :stream |
                        stream
                                do:
                                [ :each |
                                        Transcript
                                                nextPutAll: stream next;
                                                cr.
                                ].
                       
                        Transcript flush.
                ].