PipelessPipes.jl is not a package, yet, as I would be interested in hearing some opinions first. Here are my thoughts about Pipe.jl and Lazy.jl, which I have tried previously:
Pipe.jl
pros
- _ syntax is nice and non-magic
cons
- I don’t like typing
|>
- The last entry of a pipe can’t have a trailing
|>
, making it harder to comment out parts - If there’s an error in one part, the whole expression errors and VSCode can’t highlight the specific part
- end of pipeline just “dangles” in terms of indentation, which means the IDE always tries to indent also the following lines
- you can’t really interject some random statement for checking what’s going on inside the pipe that well
Lazy.jl’s @>
:
pros
- begin / end is nice for indentation after
- no
|>
necessary
cons
- Not every function needs piped thing as first argument, like
filter
(also not necessarily last, the other option@>>
) - Looks a bit too magic for me when the first argument is not visible at all
PipelessPipes.jl
edit: As I’ve made some changes since the first post, and have registered those changes now, here’s the relevant excerpt from the readme
PipelessPipes defines the @_
macro. It takes a start value and a begin ... end
block of expressions.
The result of each expression is fed into the next one using one of two rules:
- There is at least one underscore in the expression
- every
_
is replaced with the result of the previous expression
- There is no underscore
- the result of the previous expression is used as the first argument in the current expression, as long as it is a function call or a symbol representing a function.
Lines that are prefaced with @!
are executed, but their result is not fed into the next pipeline step.
This is very useful to inspect pipeline state during debugging, for example.
Example with a DataFrame:
using DataFrames, PipelessPipes
df = DataFrame(group = [1, 2, 1, 2], weight = [1, 3, 5, 7])
result = @_ df begin
filter(r -> r.weight < 6, _)
groupby(:group)
@! println("There are $(length(_)) groups after step 2.")
combine(:weight => sum => :total_weight)
end
The pipeless block is equivalent to this:
result = let
var1 = filter(r -> r.weight < 6, df)
var2 = groupby(var1, :group)
println("There are $(length(var2)) groups after step 2.")
var3 = combine(var2, :weight => sum => :total_weight)
end
For debugging, it’s often useful to look at values in the middle of a pipeline.
You can use the @!
macro to mark expressions that should not pass on their result.
For these expressions there is no implicit first argument spliced in if there is no _
, because that would be impractical for most purposes.
For the error message point, look at the difference to Pipe.jl
here from some random code of mine:
PipelessPipes.jl
Pipe.jl:
Design Questions
I wonder what the best name for the macro is, because all the good ones are already taken by other packages. I like @_
because it’s really short, although it would conflict with Underscores.jl, if one wanted to use both.
The other question is if this ruleset has some obvious problems to you. I haven’t found serious issues so far, but maybe I’ve overlooked some circumstances under which this expression parsing would break.