defmodule Quark.Partial do @moduledoc ~S""" Provide curried functions, that can also be partially bound without dot notation. Partially applying a function will always return a fully-curried function. Please note that these will use all of the arities up to the defined function. For instance: defpartial foo(a, b, c), do: a + b + c #=> foo/0, foo/1, foo/2, and foo/3 If you need to use an arity in the range below the original function, fall back to [`defcurry/2`](Quark.Curry.html#defcurry/2) and partially apply manually. """ import Quark.Curry @doc ~S""" A convenience on [`defcurry/2`](Quark.Curry.html#defcurry/2). Generates a series of partially-bound applications of a fully-curried function, for all arities _at and below_ the user-specified arity. For instance: defpartial add(a,b), do: a + b #=> add/0, add/1, add/2. ## Examples defmodule A do defpartial minus(a, b, c), do: a - b - c end A.minus(3, 2, 1) #=> 0 A.minus.(3).(2).(1) #=> 0 below_ten = A.minus(10) below_ten.(2).(1) #=> 7 below_five = A.minus(20, 15) below_five.(2) #=> 3 """ defmacro defpartial({fun_name, ctx, args}, do: body) do quote do defcurry unquote({fun_name, ctx, args}), do: unquote(body) unquote do: Enum.map(args_scan(args), &rehydrate(fun_name, ctx, &1)) end end defp rehydrate(fun_name, ctx, args) do quote do def unquote({fun_name, ctx, args}) do unquote(partial_apply(fun_name, args)) end end end @doc ~S""" `defpartial/2`, but generates private functions. """ defmacro defpartialp({fun_name, ctx, args}, do: body) do quote do defcurryp unquote({fun_name, ctx, args}), do: unquote(body) unquote do: Enum.map(args_scan(args), &rehydratep(fun_name, ctx, &1)) end end defp rehydratep(fun_name, ctx, args) do quote do defp unquote({fun_name, ctx, args}) do unquote(partial_apply(fun_name, args)) end end end defp args_scan(args), do: Enum.scan(args, [], &(&2 ++ [&1])) defp partial_apply(fun_name, args) do {as, [a]} = Enum.split(args, -1) quote do unquote(fun_name)(unquote_splicing(as)).(unquote(a)) end end end