Update: Improved ChainLink pretty-printing, to give a better idea what it does:
julia> demo" --(f; it[1]+it[2]; g(_,1)) "
--(f; #=expr_of_it=#; g(#==#))
Although a ChainLink object stores and runs a lambda (this minimizes compile time, compared with composition), I set a tuple in its type parameterization to give a basic description of the sequence of methods it calls.
julia> demo" --(f; it[1]+it[2]; g(_,1)) " |> typeof
ChainLink{var"#135#136", (:f, Symbol("#=expr_of_it=#"), Symbol("g(#==#)"))}
Of course, expressions of it can take more arbitrary behavior than simple method calls. Best practice would dictate calling named methods whenever possible (i.e., keeping expressions of it as simple and sparse as possible, trying to use them only for minor adjustments to prepare an object for the next method in the chain), so that the ChainLink’s descriptor would be most informative.
Using the baby example from comment #3:
julia> demo" --(pick_up; @assert(it.head > it.legs); put(_.butt, arm); rock) "
--(pick_up; #=expr_of_it=#; put(#==#); rock)
Creating a ChainLink out of the Chain.jl readme example:
julia> demo"""
--begin
dropmissing
filter(:id => >(6), _)
groupby(_, :group)
combine(_, :age => sum)
end"""
--(dropmissing; filter(#==#); groupby(#==#); combine(#==#))
EDIT:
I made it so now the pretty-print shows exactly how the ChainLink is constructed. (Is this a good idea or no?
):
julia> demo" --(f; it[1]+it[2]; g(_,1)) "
--(f; it[1] + it[2]; g(_, 1))
julia> demo" --(f; it[1]+it[2]; g(_,1)) " |> typeof
ChainLink{var"#175#176", (:f, Symbol("it[1] + it[2]"), Symbol("g(_, 1)"))}
julia> demo" --(pick_up; (@assert it.head > it.legs; it); put(_.butt, arm); rock(_, 2)) "
--(pick_up; begin
#= none:1 =# @assert it.head > it.legs
#= none:1 =#
it
end; put(Fix2(getproperty, :butt), arm); rock(_, 2))
julia> demo"""--begin
dropmissing
filter(:id => >(6), _)
groupby(_, :group)
combine(_, :age => sum)
end"""
--(dropmissing; filter(:id => (>)(6), _); groupby(_, :group); combine(_, :age => sum))