Incorrect Enum.reduce calls parent function

Hi everyone,

while testing a function of mine I’ve stumbled upon a strange issue - when I make a mistake in an anonymous function it might try to call the parent function recursively and throw a confusing BadArityError. Here is an example (can be pasted in a test file):

def external_fun(text) do
    dummy_list = [[one: "1", two: "2"], [one: "3", two: "4"], [one: "5", two: "6"]]
    text = Enum.reduce(dummy_list, text, fn(map, text) ->
      text = text <> Keyword.get(map, :one)
      text
    end)
    IO.inspect text
end

test "dev", %{conn: conn} do
    external_fun("Test")
end

This should be green and give a “Test135” in console. Now when I make a mistake in the anonymous function by losing the second parameter like that:

text = Enum.reduce(dummy_list, text, fn(map) ->
  text = text <> Keyword.get(map, :one)
  text
end)

I get a ** (BadArityError) #Function ... external_fun/1> with arity 1 called with 2 arguments ([one: "1", two: "2"], "Test") which is really confusing for such a “silly” mistake.

At first I thought it’s a parentheses issue (s. http://stackoverflow.com/questions/22591141/piping-maps-is-trying-to-call-the-original-function), but I already use parentheses almost all the time, so I seem to be unable to be more explicit in this case.

My questions are:

  • What actually happened? Why does reduce’s callback reaches to the parent function?

  • Is there a way to prevent such issues by changing my coding style (similar to using parentheses more often), should I maybe prefer named functions as callbacks? I’ve just spent about 20 minutes solving this issue and would like to make the most of it in the future :slight_smile:

Have not you got stacktrace back to reduce function?

yes I did, looks like this path/to/test.exs:73: TestModule.external_fun/1 where 73 is this line text = Enum.reduce(dummy_list, text, fn(map) ->

Edit: this actually makes sense to me, the full error message:

** (BadArityError) #Function<0.15934643/1 in External.external_fun/1> with arity 1 called with 2 arguments ([one: "1", two: "2"], "asdf")

It says that error is happening because you called Function<0.15934643/1 (anonymous function) in external_fun/1 with wrong arity.

1 Like

+ bunch of numbers identifies anonymous function that was called in external_fun/1 and because arity does not much you got an exception.

  text``` 

also could be just written as ```text <> Keyword.get(map, :one)```
1 Like

@sysashi @hubertlepicki that’s it, it actually makes sense, just added few dummy params to the external function to be sure, arity is still stated as 1.

Thank you guys!

1 Like

yes, obviously my code was a little more complex and I had to “reduce” it to a the minimum :slight_smile:

1 Like

also you are reusing the “text” as variable name in outer scope as in inner scope of anonymous function. This may be confusing for the reader of the code. I tend to use different names even if technically nothing wrong happens, just for readability - to distinguish those.

@hubertlepicki true, it has to do with me having to simplify my use case quickly to post here, if I wrote it from the beginning I’d probably make it sexier