Anonymous function style

I’m current learning Elixir by the book Elixir in Action 2nd Edition. On chapter 3 at Stream functions exercises i’ve solved them a bit different from the original repository.

Problem: Get the longest line length of a file

Book resoluiton:

  def longest_line_length!(path) do
    path
    |> Stream.map(&String.replace(&1, "\n", ""))
    |> Stream.map(&String.length/1)
    |> Enum.max()
  end

My resolution:

  def longest_line_length!(path) do
    File.stream!(path)
    |> Stream.map(&String.replace(&1, "\n", ""))
    |> Stream.map(&String.length(&1))
    |> Enum.max()
  end

Is there some fundamental difference between Stream.map(&String.length/1) and Stream.map(&String.length(&1)) ?

Or it is just a style preference?

Personally I think it’s more clear to use &1 even if it’s not necessary. Maybe it is just because I’m still getting used with the language. Any thoughts?

1 Like

I had the same reaction and preference when I first started learning Elixir. There may be some real difference between them that I am not aware of; I just think of it as a style difference.

Over time as I got accustomed to referring to a function as Module.function/arity, the former syntax (i.e. &String.length/1) became more natural and the latter started to feel redundant/unnecessary.

2 Likes

2 calls vs 1

Hmmm… can you explain a bit more? I’m learning yet.

I did not get how the &String.length(&1) is called twice, why is this so?

It’s not that that function is called twice, but it causes two function calls.

Using &f(&1) needs to first call the anonymous function which calls then the inner function call. Using &f/1 creates a more direct “reference” to the target function and therefore removes one level of indirection.

4 Likes

But something like this will be optimised by the compiler.
For example:

  def foo(map) do
    map
    |> Enum.map(&String.upcase/1)
    |> Enum.map(&String.upcase(&1))
  end

becomes

foo(_map@1) ->
    'Elixir.Enum':map(
      'Elixir.Enum':map(_map@1, fun 'Elixir.String':upcase/1),
      fun 'Elixir.String':upcase/1).

But &String.upcase/1 should be the preferred form.

3 Likes

Besides the points above, if you’re not reordering or transforming arguments before calling the existing function, the Mod.fun/arity style is more succinct to read and write, especially for functions with arity > 1. fun(&1, &2, &3) is repetitive in a way that isn’t productive.

3 Likes