quark
quark copied to clipboard
Add multiple anon function arities in defpartial
Current behaviour for defpartial
is to give multiple options for the first invocation, but then you have to apply single arguments after that. It should return anonymous functions with multiple heads after being invoked to give a more true partial application.
@OvermindDL1 expanded on "tuple calls" (see #25), and he also mentioned that it is going away. Returning anonymous functions with different arity heads is also an error.
iex(6)> fn
...(6)> (a) -> a
...(6)> (a,b) -> {a,b}
...(6)> end
** (CompileError) iex:6: cannot mix clauses with different arities in anonymous functions
The main problem is that only fixed arity lambdas can be returned in a function call (and this applies to macros as well).
A simple (but ugly) solution:
defmodule A do
defmacro defpartialx({fun_name, ctx, args}, do: body) do
scanned_args = [[]] ++ args_scan(args)
quote do
unquote do: make_curried_clauses(scanned_args, {fun_name, ctx, body})
end
end
defp make_curried_clauses([args], {fun_name, ctx, body}) do
quote do
def unquote({fun_name, ctx, args}), do: unquote(body)
end
end
defp make_curried_clauses([args|rest], {fun_name, ctx, _} = fun_attrs) do
quote do
def unquote({fun_name, ctx, args}) do
&apply(__MODULE__, unquote(fun_name), unquote(args) ++ List.wrap(&1))
end
unquote do: make_curried_clauses(rest, fun_attrs)
end
end
defp args_scan(args), do: Enum.scan(args, [], &(&2 ++ [&1]))
end
defmodule B do
import A
defpartialx m(a,b,c), do: a-b-c
end
iex(28)> B.m.([1,2,3,4,5])
-13
iex(29)> B.m(1,2).([3,4,5])
-13
iex(31)> B.m(1,2).([3,4]).(5)
-13
iex(32)> B.m.([1,2]).([3,4]).(5)
-13
iex(33)> B.m(1).(2).([3,4]).(5)
-13
iex(34)> B.m(1).([2,3]).([4,5])
-13
Had to re-write Quark.defpartial/2
because I couldn't figure out the way it is creating clauses.
Note to self:
Quark.defpartial/2
:
defpartial m(a,b,c), do: a-b-c
|
V
def m(), do: fn(a) -> fn(b) -> fn(c) -> a-b-c end end end
def m(a), do: m().(a)
def m(a,b), do: m(a).(b)
def m(a,b,c), do: m(a,b).(c)
# if I understood that correctly
and defpartialx/2
:
def new(), do: fn(a) -> apply(__MODULE__, :new, [a] ) end
def new(a), do: fn(b) -> apply(__MODULE__, :new, [a] ++ [b]) end
def new(a,b), do: fn(c) -> apply(__MODULE__, :new, [a, b] ++ [c]) end
def new(a, b, c), do: a - b - c
I'm facing this issue too. Trying to use partial applied function in an Enum.reduce I've got an arity exception.
@korsmakolnikov You can work around it by returning a 2-arg function from your function directly, bit of a hack but it works.
I know. The purpose of this macro is to have the strait tool as you could have in a functional ML-like-thing, I think...