Allowing the object.method(args...) syntax as an alias for method(object, args ...)

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
2 Likes