ruisin
ruisin

Reputation: 605

How to pass an anonymous function to the pipe in Elixir

I'd like to write the code like this:

def boundary do
  :crypto.rand_bytes(8)
  |> Base.encode16
  |> &("--------FormDataBoundary" <> &1)
end

But it doesn't work.

Upvotes: 48

Views: 14995

Answers (6)

ndrdm
ndrdm

Reputation: 797

Elixir 1.12 introduced macro then(value, fun). Citing the docs:

then(value, fun)

Pipes value into the given fun.

In other words, it invokes fun with value as argument. This is most commonly used in pipelines, allowing you to pipe a value to a function outside of its first argument.

Your code becomes:

:crypto.strong_rand_bytes(8)
|> Base.encode16
|> then(&("--------FormDataBoundary" <> &1))

I'm using :crypto.strong_rand_bytes/1 above because :crypto.rand_bytes/1 was deprecated in Erlang/OTP 18.

Upvotes: 6

Kenny Evitt
Kenny Evitt

Reputation: 9791

You can also use something like this:

def boundary do
  :crypto.rand_bytes(8)
  |> Base.encode16
  |> (fn chars -> "--------FormDataBoundary" <> chars end).()
end

One advantage of this form over others is that you can easily write simple 'case' statements:

def do_some_stuff do
  something
  |> a_named_function()
  |> (
    fn
      {:ok, something} -> something
      {:error, something_else} ->
        Logger.error "Error"
        # ...
    end
  ).()
end

From:


Using fn as above is a tiny bit clearer than couchemar's answer:

def boundary do
  :crypto.rand_bytes(8)
  |> Base.encode16
  |> (&("--------FormDataBoundary" <> &1)).()
end

... but, for your particular example, the above form using & is probably best. If the pipeline expression was more complicated, naming the anonymous function parameters might be more useful.

My answer is also a little more concise than Nathan Long's answer:

def boundary do
  add_marker = fn (s) ->
    "--------FormDataBoundary" <> s
  end

  :crypto.rand_bytes(8)
  |> Base.encode16
  |> add_marker.()
end

... tho his answer would be nicer if, for some reason, you needed to call that function more than once in the pipeline.

Upvotes: 2

Ian
Ian

Reputation: 562

can't you literally just go?

thing
|> func_one()
|> fn input -> do_stuff_here() end)

You can do stuff like piping things directly into case like

thing
|> func_one()
|> case do

so, I would think you can just pipe into an anonymous function.

Upvotes: 1

Harlan T Wood
Harlan T Wood

Reputation: 2434

The accepted answer works, but you can do this a bit more elegantly by using

(&"--------FormDataBoundary#{&1}").()

instead of

(&("--------FormDataBoundary" <> &1)).()

Here is the full function:

def boundary do
  :crypto.strong_rand_bytes(8)
  |> Base.encode16()
  |> (&"--------FormDataBoundary#{&1}").()
end

Bonus: I've also replaced :crypto.rand_bytes/1 (which doesn't exist in elixir 1.6+) with :crypto.strong_rand_bytes/1.

Upvotes: 4

Nathan Long
Nathan Long

Reputation: 125902

Related: if the "anonymous" function has been assigned to a variable, you can pipe to it like this:

def boundary do
  add_marker = fn (s) ->
    "--------FormDataBoundary" <> s
  end

  :crypto.rand_bytes(8)
  |> Base.encode16
  |> add_marker.()
end

Upvotes: 17

couchemar
couchemar

Reputation: 1947

It will look bit weird but must work:

def boundary do
  :crypto.rand_bytes(8)
  |> Base.encode16
  |> (&("--------FormDataBoundary" <> &1)).()
end

Upvotes: 80

Related Questions