Functional application, reconciling currying with multiple dispatch

This is a pretty hefty proposal. I do think we need better ways to succinctly express partial evaluation of functions, but honestly to my eye, I don’t really like any of the proposed options here. I find nearly all of them pretty hard to read. Honestly, I think julia already has a gigantic amount of syntactic forms so my default position is one of being skeptical of adding more.

I think the main difficulty I have with your proposal is that one specifies that they want currying at the call-site, meaning that these special syntactic forms are required each time we want to call a curried function, which leads to a big bloat of arcane symbols all over the place.

An alternative approach is just to annotate a function definition (instead of call) to say that we want that function to have currying behaviour.

For instance, we could imagine creating a macro @curried such that

@curried foo(x, y, z) = (x^2 + y^2)/(x^2 + y^2 +z^2)

will define extra methods such that foo(x)(y)(z) is equivalent to foo(x, y, z).

The macro could be written as follows:

struct FullyCurried end

macro curried(fdef)
    f = fdef.args[1].args[1]
    fargs = fdef.args[1].args[2:end]
    arity = length(fargs)
    body = fdef.args[2]
    err_str = "Too many arguments. Function $f only takes $arity arguments"
    quote 
        begin 
        function $f(args...)
            if length(args) < $arity
                x -> $f((args..., x)...)
            elseif length(args) == 3
                $f(FullyCurried(), args...)
            else
                throw($err_str)
            end
        end
        $f(::FullyCurried, $(fargs...)) = $body
        end
    end |> esc
end

With that, we can do

julia> @curried foo(x, y, z) = (x^2 + y^2)/(x^2 + y^2 +z^2)
foo (generic function with 2 methods)

julia> foo(1)(2)(3)
0.35714285714285715

julia> foo(1, 2, 3)
0.35714285714285715

and some benchmarking / looking at LLVM code confirms that there is no runtime overhead to the curried form versus the non-curried form. This approach requires no modifications to julia’s parser which I quite like.

13 Likes