Pipe operator and functions (not function calls)

I keep finding myself wanting to do this:

def foo(m1, m2) do
  m1
    |> transform("some arg")
    |> &other_func(m2, &1)
end

Which doesn’t work because |> only works with function calls, not functions.

If you convert it to a function call, it works! But the syntax is ugly.

def foo(m1, m2) do
  m1
    |> transform("some arg")
    |> &(other_func(m2, &1)).()
end

Is there any reason to not make the first (prettier) form work? Basically, modify pipe operator to “if receive a function of arity 1, call the function passing in the arg”.

Or is there a better way to do what I’m trying to do? Thanks!

2 Likes

Im really not an expert, but if you can’t simply do

def foo(m1, m2) do
  m1
    |> transform("some arg")
    |> other_func(m2)
end

Because m2 is the first argument of the function, I would probably made a helper function like this

def other_func_helper(m1, m2) do
  other_func(m2, m1)
end

Of course i probably would think more about the naming. And as I said i’m not an expert, might misunderstood something, etc :wink:

Ps. and if arguments are of different type I would probably do even

def other_func(m1, m2) when is_TYPE(m1) do # by type I mean list, map, whatever is good in your context.
  other_func(m2, m1)
end
3 Likes

I’m not clear about what you’re trying to do either. Are you doing this simply to put the previously piped value as the 2nd argument?

1 Like

Yes, so you can keep the pipe going:

foo
  |> transform1("data1")
  |> transform2("data2")
  |> &transform_with_args_in_wrong_order("data3", &1)
  |> transform4("data4")

This has been discussed in various forms at great length on the core mailing list. Basically, write a small helper function, or break up the pipeline.

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)

I feel you already have the solution there… If you find the .() too ugly just use a normal function wrapping your functionality. I think there is no better way, and the reason for not making the pipe is related to a discussion over lisp1 vs lisp2 function calls. Elixir just settles on one.

For instance, if you could have a variable contain an anonymous function in Elixir with zero arguments, it would be very difficult for Elixir to know when that function should be called or be used as a value, as the parenthesis may be omitted.

2 Likes

There are really two subject matters at work here.

foo
|> bar.()

just isn’t that bad. What’s ugly is

foo
|> &(bar(x, &1).()
```

but this latter problem simply stems from a wrong approach to piping in the first place.

As has been covered extensively in the relevant mailing list threads, the solution is to either:

```
thing =
  foo
  |> bar
  |> baz

other_value
|> fun(thing)
|> etc
```

OR
```
def helpfully_named_fun(piped_value, other_value), do: fun(other_value, piped_value)

foo
|> bar
|> baz
|> helpfully_named_fun(other_value)
|> etc
```

Which is better depends on what produces the most readable code.
2 Likes