Nice example of how an argument can be inserted at any position. Just to try out the new syntax, I have hacked together a small macro which rewrites ++
(a valid and as of now unused operator) into right-associative calls of minusminus
(our higher-order function corresponding to the invalid operator --
):
using MacroTools: postwalk, @capture
minusminus(obj, meth) = (args...; kwargs...) -> meth(obj, args...; kwargs...)
function unchain(terms::AbstractVector, stack::AbstractVector)
if isempty(terms)
:(foldr(minusminus, [$(stack...)]))
elseif @capture(terms[1], f_(args__)) && f != :foldr # don't touch nested transforms again
arg = :(foldr(minusminus, [$(stack...), $f])($(args...)))
unchain(terms[2:end], push!([], arg))
else
unchain(terms[2:end], push!(stack, terms[1]))
end
end
macro calumet(expr)
postwalk(expr) do ex
if @capture(ex, ++(args__))
unchain(args, [])
else
ex
end
end
end
Both of your examples now work as follows:
julia> @calumet "Hello, world!"++split(",")++uppercase++map()++join(":")
"HELLO: WORLD!"
julia> @calumet let x = [1,2,missing,4,5,missing]; x++skipmissing()++isodd++filter() end
2-element Vector{Int64}:
1
5
Also nested transformations should work, but I have not tested extensively:
julia> @calumet 1 ++ 2 ++ Base.:+() ++ (3 ++ 4 ++ Base.:+()) ++ Base.:*()
21