I’m putting this here for discussion because I’m not sure how well it’ll be received. But with tap/2
& then/2
introduced in 1.12 I’m hoping there’s at least some room for discussion.
The pipe operator already supports passing into anonymous functions using the long-form syntax:
iex> :foo |> (fn x -> to_string(x) end).()
"foo"
We can also wrap named functions this way if we really want to:
iex> 1 |> (&Enum.at([1, 2, 3], &1)).()
2
Which has the same outcome as:
iex> 1 |> then(&Enum.at([1, 2, 3], &1))
2
But even these simple examples are quite verbose. And for a while I’ve been importing a macro based on an experiment by Qqwy to override the pipe/3
& unpipe/4
functions, and I’m hoping something similar can make it into upstream.
Using Qqwy’s original override, I added it to master branch as an example and to see if there were any unintended side effects. Essentially it pattern matches on calls beginning with &
or fn
and wraps them in the long-form syntax above. I personally use it to avoid a lot of named assigns, although tap & then solve a lot of these problems as well. The examples above become:
iex> :foo |> fn x -> to_string(x) end
"foo"
iex> 1 |> &Enum.at([1, 2, 3], &1)
2
For a more real world example, let’s do something with XmlBuilder
. By extending pipe to accept captures, we can go from:
defp build_contacts(directory) do
title = element(:title, directory.name)
contacts =
for contact <- directory.contacts do
element(:DirectoryEntry, [
element(:Name, contact.name)),
for tel <- contact.contacts do
element(:Telephone, %{label: tel.type}, tel.number)
end
])
end
document([
element(:Directory, [
title,
contacts
])
])
|> generate()
end
To a more readable:
defp build_contacts(directory) do
title = element(:title, directory.name)
contacts =
for contact <- directory.contacts do
for tel <- contact.numbers, do: element(:Telephone, %{label: tel.type}, tel.number)
|> &[element(:Name, contact.name), &1]
|> &element(:DirectoryEntry, &1)
end
[title, contacts]
|> &element(:Directory, &1)
|> &document([&1])
|> generate()
end
It’s true that the capture version could be achieved much the same way with then/2
, which should largely be the argument against there suggested patch, but I honestly believe that &
and fn
improve readability and are much easier to quickly scan through rather than buried in then
calls.
What are other people’s thoughts? Am I the outlier on this one?