Elixir Tricks – Piping into anonymous functions
September 1, 2017
elixir
Problem: Pipes require the “subject” to be the first parameter in your piped functions
When writing Elixir code, I love making elegant pipelines the core of my function:
this
|> then_this()
|> this_again()
This requires that functions you place in the pipeline take the “subject” parameter as the first parameter. Erlang doesn’t have the pipe operator and for that reason, a good number of the functions in Erlang libraries put the “subject” at the end or elsewhere, making it hard to include in a pipe.
However, I recently discovered a magic trick that allows me to elegantly solve this problem and reorder my function parameters in a pipe. Here’s how it works:
Solution: Pipe through anonymous functions
Let’s say there’s a function I want to call in my pipe…
def foo(key, thing)
…but I want my pipe to feed into the thing
parameter of foo
.
I can create and call an anonymous function inside my pipe to reorder the parameters.
this
|> then_this()
|> this_again()
|> (&foo(:key, &1)).()
Neat huh?
Application: Status tuples
You can also use this when you want to pipe into a status tuple, changing…
result = this
|> then_this()
|> this_again()
{:ok, result}
…into…
this
|> then_this()
|> this_again()
|> (&{:ok, &1}).()
Application: Debugging with IO.puts/1
One more super handy usage of this is when puts-debugging.
I’ll often stick IO.inspect/1
into a pipe so I can output the contents of the pipe at a certain point in the pipeline. It works because IO.inspect/1
returns its input after printing it out.
this
|> then_this()
|> IO.inspect()
|> this_again()
|> IO.inspect()
However, it also doesn’t print anything else which sometimes makes it hard to mark specific points in the logging output.
If you turn IO.inspect/1
into an anonymous function, you can use IO.puts/1
and add some context. It looks like this:
|> fn i -> IO.puts("extra message: #{inspect i}"); i end.()
Don’t forget to add the ; i
to the end of your anonymous function or you won’t pass the input through. Also, the &(&1)
form of anonymous functions only allows a single statement so we have to use the fn -> end
version (still worth it).
Here it is in context:
this
|> then_this()
|> fn i -> IO.puts("then_this: #{inspect i}"); i end.()
|> this_again()
|> fn i -> IO.puts("this_again: #{inspect i}"); i end.()
Enjoy!