12
votes

In R, with dyplr you have the %>% operator which allows you to pipe the output of a function to a new function, removing the need to store intermediate values. In julia you can achieve something very similar by using the |> operator.

Example usage

2 |> log |> sqrt 

To me this reads a lot nicer than sqrt(log(2)). Especially when the chain becomes very long. I would like to use this syntax but also for map,reduce-type functions in Julia.

Setup

from = "abcdefghijklmnopqrstuvwxyz"
to   = "cdefghijklmnopqrstuvwxyzab"
trans = "g fmnc wms bgblr rpylqjyrc gr zw fylb <>"
d = {from[i] => to[i] for i = 1:26}
d[' '] = ' '

What works

map(x -> d[x], filter(x -> isalpha(x) || isspace(x), trans))

This works, but it doesn't read as well as I would like it to. An alternative would be to store intermediate results into variables, but this also seems verbose:

res1 = filter(x -> isalpha(x) || isspace(x), trans)
map(x -> d[x], res1)

What I would prefer

The R syntax would be something similar to this:

trans |> 
  filter(x -> isalpha(x) || isspace(x)) |> 
  map(x -> d[x])

This will not work because in Julia the mapping/filter function goes before the iterable. R would solve this by giving you an infix . that you can use, in which case the syntax would look a bit more like:

trans |> 
  filter(x -> isalpha(x) || isspace(x), .) |> 
  map(x -> d[x], .)

Is something like this possible in Julia? The |> operator has the potential to clean up long chains of code into neat pipes of operations. A perhaps more Julia-thonic way of a approaching the problem would also be considered an answer to this question.

4
I've found chaining and pipes very convenient for data analysis. Saves us from all the going-to-the-beginning-of-the-line and adding another function, and then going-to-the-end-of-the-line to add the right number of close parentheses, etc. - rickhg12hs
@rickhg12hs agreed. The flipside of course is that, same as if you've got a line with multiply-nested function calls, if you're relying on multiple chaining / redirections instead, particularly involving bespoke anonymous functions created for the sole purpose of achieving the redirection, chances are you could be writing code in discrete steps which would be a lot clearer to read (not to mention, amenable to proper commenting). But I agree, between nesting and chaining, chaining is much more pleasant visually. - Tasos Papastylianou

4 Answers

12
votes

Not exactly sure what you're looking for, but:

SPOILER ALERT 8-)

julia> trans |> 
         t -> filter(x -> isalpha(x) || isspace(x), t) |> 
         f -> map(x -> d[x],f)
"i hope you didnt translate it by hand "
7
votes

You can also use the @as macro from the Lazy package to get a little closer to your preferred form:

julia> using Lazy

julia> @as _ trans begin
         filter(x -> isalpha(x) || isspace(x), _)
         map(x -> d[x], _)
       end
"i hope you didnt translate it by hand "
3
votes

You can you the Pipe.jl package to do it like this:

@pipe trans |> filter(x -> isalpha(x) || isspace(x), _) |> map(x -> d[x], _)

Or

@pipe ( trans 
     |> filter(x -> isalpha(x) || isspace(x), _) 
     |> map(x -> d[x], _)
)

The brackets around the arguments to the macro are needed if it goes onto multiple lines.

NB: I am the creator and maintainer of Pipe.jl

0
votes

Slightly different than the others, but I like

using Lazy

from = "abcdefghijklmnopqrstuvwxyz"
to   = "cdefghijklmnopqrstuvwxyzab"
trans = "g fmnc wms bgblr rpylqjyrc gr zw fylb <>"

dd = Dict(zip(from, to))
@>> trans map(t -> isletter(t) ? dd[t] : t)

which yields

"i hope you didnt translate it by hand <>"