Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multiple anon function arities in defpartial #12

Open
expede opened this issue Oct 21, 2016 · 5 comments
Open

Add multiple anon function arities in defpartial #12

expede opened this issue Oct 21, 2016 · 5 comments

Comments

@expede
Copy link
Member

expede commented Oct 21, 2016

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.

@toraritte
Copy link
Contributor

@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).

@toraritte
Copy link
Contributor

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

@korsmakolnikov
Copy link

I'm facing this issue too.
Trying to use partial applied function in an Enum.reduce I've got an arity exception.

@OvermindDL1
Copy link

@korsmakolnikov You can work around it by returning a 2-arg function from your function directly, bit of a hack but it works.

@korsmakolnikov
Copy link

korsmakolnikov commented Apr 8, 2020

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...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants