I'm just working through Programming Elixir and implementing Split.
My code as is follows -
defmodule MyEnum do
def split(l, n) do
def split_helper([], pre, _n), do: {pre, []}
def split_helper(l, pre, 0), do: {pre, l}
def split_helper([h|t], pre, n), do: split_helper(t, [h|pre], n-1)
split_helper(l, [], n)
end
end
and I am getting the error - cannot invoke def/2 inside function/macro
Which I take to mean I can't nest named functions. As you can't write recursive anonymous functions and there is no letrec, I am wondering how to nest functions where the nested function is recursive. This is the way I would structure my code in racket as I would never need to use split_helper anywhere else.
Functions in Elixir cannot be nested. Period.
The idiomatic way to implement this would be to have a private function called do_split/3.
defmodule MyEnum do
def split(l, n), do: do_split(l, [], n)
defp do_split([], pre, _n), do: {pre, []}
defp do_split(l, pre, 0), do: {pre, l}
defp do_split([h|t], pre, n), do: do_split(t, [h|pre], n-1)
end
Another way to approach the problem would be to simply have several clauses of split/3 itself.
defmodule MyEnum do
def split(l, acc \\ [], n) # default values
def split([], pre, _n), do: {pre, []}
def split(l, pre, 0), do: {pre, l}
def split([h|t], pre, n), do: split(t, [h|pre], n-1)
end
The least elegant, but still working approach would be to create anonymous function and pass it through.
defmodule MyEnum do
def split(l, n) do
split_helper = fn
[], pre, _, _ -> {pre, []}
l, pre, 0, _ -> {pre, l}
[h|t], pre, n, split_helper ->
split_helper.(t, [h|pre], n-1, split_helper)
end
split_helper.(l, [], n, split_helper)
end
end