defmodule Postfix do @moduledoc """ Documentation for `Postfix`. """ @doc """ Evaluate a list of values and functions iex> Postfix.eval([2, 3, &*/2, 4, &+/2]) {:ok, 10} """ @spec eval([term]) :: {:ok, term} | {:error, any} def eval([f]) when is_function(f) do eval_f(f) end def eval([v]) do {:ok, v} end def eval([f | rest]) when is_function(f) do with {:ok, r} <- eval_f(f) do eval([r | rest]) end end def eval([v | [f | rest]]) when is_function(f) do with {:ok, r} <- eval_f(fn -> f.(v) end) do eval([r | rest]) end end def eval([v1 | [v2 | [f | rest]]]) when is_function(f) do with {:ok, r} <- eval_f(fn -> f.(v1, v2) end) do eval([r | rest]) end end def eval([v1 | [v2 | [v3 | [f | rest]]]]) when is_function(f) do with {:ok, r} <- eval_f(fn -> f.(v1, v2, v3) end) do eval([r | rest]) end end def eval([v1 | [v2 | [v3 | [v4 | [f | rest]]]]]) when is_function(f) do with {:ok, r} <- eval_f(fn -> f.(v1, v2, v3, v4) end) do eval([r | rest]) end end # Arrity 5..255 def eval(terms) do not_function = fn x -> !is_function(x) end case Enum.split_while(terms, not_function) do {args, [f | rest]} -> with {:ok, r} <- eval_f(fn -> apply(f, args) end) do eval([r | rest]) end {rest, []} -> {:ok, rest} end end defp eval_f(f) do case f.() do {:error, e} -> {:error, e} {:ok, v} -> {:ok, v} v -> {:ok, v} end end end