* Avoid do_ prefixing
* Refactor list functions to be body-recursive where sensible
The reasoning is the same as in #3811 and 99e0d8e.
* Use recursive functions to optimize filter/reject for lists
----- With input: Big (10 Million) -----
Name ips average deviation median
tail 1.64 610.99 ms ±13.78% 577.63 ms
body 1.48 675.90 ms ±10.06% 681.08 ms
for 1.46 687.19 ms ±12.84% 693.82 ms
Comparison:
tail 1.64
body 1.48 - 1.11x slower
for 1.46 - 1.12x slower
----- With input: Medium (100 Thousand) -----
Name ips average deviation median
tail 201.60 4.96 ms ±15.12% 4.81 ms
body 199.52 5.01 ms ±14.03% 4.76 ms
for 178.95 5.59 ms ±14.50% 5.39 ms
Comparison:
tail 201.60
body 199.52 - 1.01x slower
for 178.95 - 1.13x slower
---- With input: Small (1 Thousand) -----
Name ips average deviation median
body 23.98 K 41.70 μs ±38.90% 38.00 μs
tail 21.35 K 46.84 μs ±35.64% 44.00 μs
for 18.64 K 53.63 μs ±31.60% 50.00 μs
Comparison:
body 23.98 K
tail 21.35 K - 1.12x slower
for 18.64 K - 1.29x slower
* Add function guard to group_by/3 back
It was used for dispatch between the new and deprecated version.
The guard was erroneously removed in https://github.com/elixir-lang/elixir/commit/99e44a181fb3440ef572be2f9592c94aa0a99ebc#diff-6881431a92cd4e3ea0de82bf2338f8eaL1032.
When default arguements (\\) are used with defdelegate/2, a function is
generated for each default argument and each generated function calls the
delegate target function with the same arity. For example:
defdelegate demonitor(monitor_ref, options \\ []), to: :erlang
will be expanded to
def demonitor(monitor_ref), do: :erlang.demonitor(monitor_ref)
def demonitor(monitor_ref, options), do: :erlang.demonitor(monitor_ref, options)
However, this seems to "break" the expectation that optional arguments set: when
a function has optional arguments, all the additional functions with lower arity
that are generated by the default arguments only call the function with maximum
arity with the default values provided for the optional arguments.
This means that if :erlang.demonitor/1 had a different behaviour than
:erlang.demonitor/2, then it would be hard to guess why because the expectation
is that demonitor(monitor_ref) only calls demonitor(monitor_ref, []), ending up
always to :erlang.demonitor/2.
This commit fixes this by leaving the signature of the functions to delegate
untouched (with optional arguments if present).