On 8 June 2014 19:46, Camille Teruel <camille.ter...@gmail.com> wrote:
>
> On 8 juin 2014, at 19:20, Garth Holland <steve9571...@hotmail.com> 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 via email to