Cannot pipe [1, 2, 3] into &(IO.inspect(&1))

I thought I understood piping with |> as well as the & notation for creating anonymous functions, but the following doesn’t work:

iex(1)> [1, 2, 3] |> &IO.inspect/2
** (ArgumentError) cannot pipe [1, 2, 3] into &IO.inspect/2, can only pipe into local calls foo(), remote calls Foo.bar() or anonymous functions calls foo.()
    (elixir) lib/macro.ex:118: Macro.pipe/3
    (stdlib) lists.erl:1263: :lists.foldl/3
    (elixir) expanding macro: Kernel.|>/2
    iex:1: (file)

I get the same error trying to create the anonymous function like this:

iex(2)> [1, 2, 3] |> &(IO.inspect &1)
** (ArgumentError) cannot pipe [1, 2, 3] into &(IO.inspect(&1)), can only pipe into local calls foo(), remote calls Foo.bar() or anonymous functions calls foo.()
    (elixir) lib/macro.ex:118: Macro.pipe/3
    (stdlib) lists.erl:1263: :lists.foldl/3
    (elixir) expanding macro: Kernel.|>/2
    iex:2: (file)

Yet, the docs say:

It’s important to note that [IO.inspect/2] returns the given item unchanged. This makes it
possible to “spy” on values by inserting an IO.inspect/2 call almost anywhere
in your code, for example, in the middle of a pipeline.

Never mind. The syntax should be:

iex(3)> [1, 2, 3] |> IO.inspect    #equivalent to IO.inspect([1,2,3])                                                                            
[1, 2, 3]
[1, 2, 3]

You use the & notation when you want to send an anonymous function wrapping IO.inspect/2 as an argument to another function, e.g. Enum.map(list, &IO.inspect/1)

Well, you can in fact use anonymous functions with pipes, but like this:

[1, 2, 3] |> (&(IO.inspect &1)).()

…or:

[1, 2, 3] |> (fn x -> IO.inspect x end).()

It’s a little more obvious to see what was wrong with your initial syntax if you include the parentheses:

[1, 2, 3] |> &IO.inspect/2 # wrong
[1, 2, 3] |> IO.inspect()    # correct

Piping requires function calls, but in the first case you were trying to send the data to a function reference/definition. That’s why the following works as @jwarlander said (because they’re calls to anonymous functions as you’ll notice with the .()):

 [1, 2, 3] |> (&(IO.inspect &1)).()
 [1, 2, 3] |> (fn x -> IO.inspect x end).()

I’ve written a bit more about it on my blog here (where you can also find other useful pattern snippets)

6 Likes

Yeah, Elixir’s Pipe is not like a pipe in any other language that I can think of, it pipes into a call rather than piping ‘as’ a call. ^.^;