47

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.

ruisin
  • 595
  • 1
  • 4
  • 7

6 Answers6

78

It will look bit weird but must work:

def boundary do
  :crypto.rand_bytes(8)
  |> Base.encode16
  |> (&("--------FormDataBoundary" <> &1)).()
end
couchemar
  • 1,927
  • 19
  • 22
  • 4
    is there any reason for it to be so weird? When I pass a function to a pipe I treat it more like a variable (yes, I'm from Javascript), but doing (fn).() looks like I'm immediately invoking it which I am not. – leitasat Apr 08 '16 at 10:16
  • 1
    `(&("--------FormDataBoundary" <> &1)).()` It looks like you define an anonymous function that takes 1 argument, then call it with zero arguments, and then it's part of the pipeline? I don't understand why you need the `.()` at the end. Isn't this calling the anonymous function? I don't want to call it myself, I want it to be part of the pipeline. – Buttons840 Apr 05 '17 at 22:54
  • I think I get it now. The fact that the function is part of a pipeline means that the pipeline supplies the first argument. All that is left is for the function to be called, which you do with the `.()`. – Buttons840 Apr 05 '17 at 23:01
  • 1
    I struggled with this at first. Then I realized when you use pipelines, every function in the pipeline is a function call, not a function reference. This is obvious if the functions in your pipeline have arity 2+, since then you _have_ to use parentheses for the named functions too. Making the function call with parentheses for the anonymous function isn't weird once you realize all of the named functions are explicitly called also. If their arity is 1, you can omit the parentheses (makings it _look_ like a function reference rather than a function call, but it's a function call nonetheless.) – Nathan Jan 25 '18 at 05:18
16

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
Nathan Long
  • 122,748
  • 97
  • 336
  • 451
4

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.

Harlan T Wood
  • 2,064
  • 21
  • 19
2

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.

Kenny Evitt
  • 9,291
  • 5
  • 65
  • 93
2

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.

Andrei Duma
  • 717
  • 7
  • 10
1

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.

Ian
  • 544
  • 3
  • 16