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!

comments powered by Disqus