Anonymous function shorthand ignores outer parens

136 views
Skip to first unread message

Ben Wilson

unread,
Jun 19, 2015, 10:34:27 AM6/19/15
to elixir-l...@googlegroups.com
Anonymous functions when present in pipes like the following

"this" |> &(&1).()
"this" |> &("#{&1} fails").()

All fail with errors like

# "this" |> &(&1).()
** (ArgumentError) cannot pipe "this" into &&1.(), can only pipe into local calls foo(), remote calls Foo.bar() or anonymous functions calls foo.()
    (elixir) lib/macro.ex:95: Macro.bad_pipe/2
    (stdlib) lists.erl:1261: :lists.foldl/3
    (elixir) expanding macro: Kernel.|>/2

** (ArgumentError) cannot pipe "this" into &<<Kernel.to_string(&1) :: binary, " fails">>.(), can only pipe into local calls foo(), remote calls Foo.bar() or anonymous functions calls foo.() (elixir) lib/macro.ex:95: Macro.bad_pipe/2 (stdlib) lists.erl:1261: :lists.foldl/3 (elixir) expanding macro: Kernel.|>/2 iex:3: (file)

This seems to indicate that the outer parens are being stripped for some reason. The following however works:

foo = &(&1); "this" |> foo.() #=> this

This looks like a bug to me, but I'm not entirely sure what kind (parser, expansion, etc).

- Ben

José Valim

unread,
Jun 19, 2015, 10:41:56 AM6/19/15
to elixir-l...@googlegroups.com
When you write:

"this" |> &(&1).()

You are piping into the & operator. So Elixir is correctly complaining. We *could* fix this but I truly believe you should be rather using a named variable or a private function or at least wrapping the parenthesis externally:

"this" |> (& &1).()



José Valim
Skype: jv.ptec
Founder and Director of R&D

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/ee67abdb-79db-451c-8417-89d68508aa23%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ben Wilson

unread,
Jun 19, 2015, 11:40:55 AM6/19/15
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
AH right ok that makes sense.

Hassan Zamani

unread,
Jul 10, 2015, 10:00:01 AM7/10/15
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
Why we can't pipe into & operator? e.i why Macro.pipe for {&, _, _} is a bad_pipe and not something like this:

def pipe(expr, {:&, _, _} = call_args) do
  call_args = {{:., [], [call_args]}, [], []}
  pipe(expr, call_args)
end

which will make the following work:

"This" |> &IO.puts/1
10 |> &(&1 + 10)

José Valim

unread,
Jul 10, 2015, 10:09:24 AM7/10/15
to elixir-l...@googlegroups.com
Because pipe pipes into function calls, it doesn't pipe into functions. When you have &IO.puts/1, you have a function, you don't have a function call. It could behave as you proposed if we had currying but we don't so we need to stick with the macro semantics.



José Valim
Skype: jv.ptec
Founder and Director of R&D

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.

Hassan Zamani

unread,
Jul 10, 2015, 11:10:56 AM7/10/15
to elixir-l...@googlegroups.com
What is wrong with piping both to fuctions and fuction calls? What kind of bugs it may cause?

Robert Virding

unread,
Jul 10, 2015, 11:29:51 AM7/10/15
to elixir-l...@googlegroups.com
What meaning would it have if you piped into a function? The whole of the pipe operator is to pass data between function calls.

Robert

Hassan Zamani

unread,
Jul 10, 2015, 11:40:04 AM7/10/15
to elixir-l...@googlegroups.com
It would mean to call that function with given arg

--
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/R92btA1X1gg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/c349ff53-4c90-413b-b9f9-cec853c8fcab%40googlegroups.com.

Jason M Barnes

unread,
Jul 10, 2015, 12:45:27 PM7/10/15
to elixir-l...@googlegroups.com
A contrived example, but what if we have a couple of functions defined like so:

def func(), do: fun item -> … end
def func(item) do
  # do something with the item
end

Now we want to pipe something to the function:

:item |> func

If we allow piping to both functions and function calls, the meaning is ambiguous.  Do we mean to evaluate func/0 first to return the anonymous function, or do we pass :item directly to func/1?

I think limiting pipes to function calls keeps the code less ambiguous and easier to read.

Jason

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CALEByXbjyDWFC7%2BmZmkgECQQ8CYQgwrhiMQ7%2Bjo-0TSh0rcMSg%40mail.gmail.com.

Hassan Zamani

unread,
Jul 10, 2015, 1:23:28 PM7/10/15
to elixir-l...@googlegroups.com

As in the code above, by function I mean a capture, i.e. &func/1, and it specifies the ariry, so there will be no ambiguity.
Also that little peace of code do nothing but calling the capture, and as a result the code is nicer on the eye.

Jason M Barnes

unread,
Jul 10, 2015, 1:32:43 PM7/10/15
to elixir-l...@googlegroups.com
But then you’re typing more:

“This” |> &IO.puts/1

versus:

“This” |> IO.puts

I don’t see what this gets you.

José Valim

unread,
Jul 10, 2015, 1:41:19 PM7/10/15
to elixir-l...@googlegroups.com
The issue is that you are asking to special case *some functions*. Only functions that can be seen as functions during compile time would work. If you have a function at runtime, it won't work, and that will be ultimately confusing.

That's why pipe works on *calls*. Calls are compile time constructs by definition. You are asking for special cases to be added to a macro which is already very well defined.


José Valim
Skype: jv.ptec
Founder and Director of R&D

Hassan Zamani

unread,
Jul 10, 2015, 1:48:54 PM7/10/15
to elixir-l...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages