Proposal: Pipe-assign operator.

450 views
Skip to first unread message

ja...@rabidtech.co.nz

unread,
Feb 25, 2016, 2:24:32 PM2/25/16
to elixir-lang-core
Hi everyone.

I find that the assignment with pipe syntax can be a little confusing:

result = widget
|> map
|> filter
|> reduce


And I was wondering if we could make a "pipe assign" operator so that the above code could be written more like:

widget
|> map
|> filter
|> reduce
|= result


so that it would be immediately clear to the casual reader that the assignment is the value and the end of the pipeline, not at the beginning.

Thanks.

---
James Harton
Professional Tinkerer, Rabid

Chris McCord

unread,
Feb 25, 2016, 2:37:30 PM2/25/16
to elixir-l...@googlegroups.com
This breaks the semantics of both variable binding and pattern matching, so I am -1 on it. I write bindings like this when a pipeline:

result = 
  widget
  |> map
  |> filter
  |> reduce

I also think the answer to doing this kind of thing too much and it being harder to follow is that it's an indication that you should extract the pipeline to a function:

result = widget_values(widget)



--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/8da65aa0-7d66-430f-8a53-fdf3baa39c9a%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

José Valim

unread,
Feb 25, 2016, 2:38:20 PM2/25/16
to elixir-l...@googlegroups.com
Thank you but we have no plans of adding more pipe like operators or extend the pipe syntax.

A quick search through this mailing list will find at least 3 different "extensions" for the pipe operator just in the last month.

The pipe works because it is simple, let's please keep it this way. :)




José Valim
Skype: jv.ptec
Founder and Director of R&D

--

James Harton

unread,
Feb 25, 2016, 2:39:04 PM2/25/16
to elixir-l...@googlegroups.com, elixir-l...@googlegroups.com
Roger that. Thanks for the feedback :)

James Harton
Professional Tinkerer, Rabid
0226803869 | http://rabid.co.nz/

You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/KTqQ1aGcJFU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4J_mxv%2BKdbBfRbJQzs996M72ydGNVVA4HEt7g0b19fPJA%40mail.gmail.com.

Onorio Catenacci

unread,
Feb 25, 2016, 3:09:15 PM2/25/16
to elixir-l...@googlegroups.com
This is partially learning to think in the functional paradigm James. In functional, every construct returns a value.  Hence it makes sense to assign at the beginning of the expression.  

Viewed another way:

r = x 
|> f
|> g
|> h

is syntactic sugar for 

r = f(g(h(x))) 

It's just easier to follow the logic of the pipes because it reads more in the fashion we're accustomed to thinking.  Semantically though, they're equivalent.

--
Onorio



--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/8da65aa0-7d66-430f-8a53-fdf3baa39c9a%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Constantin Rack

unread,
Feb 25, 2016, 4:44:41 PM2/25/16
to elixir-l...@googlegroups.com
Sorry for being nitpicky, but the equivalent for the pipe syntax would be:

r = h(g(f(x)))

-Constantin


Onorio Catenacci

unread,
Feb 25, 2016, 4:53:48 PM2/25/16
to elixir-l...@googlegroups.com
Thanks for correcting me.  Details are extremely important in our line of work. :)

--
Onorio



For more options, visit https://groups.google.com/d/optout.

Tallak Tveide

unread,
Feb 26, 2016, 1:11:36 PM2/26/16
to elixir-lang-core
I was actually thinking about this this morning. It does seem a bit awkward to me that the pipe flows from top to bottom, but the last operation which might be an assignment is at the very top, ie:

```
result =
    [1, 2, 3, 4]
    |> Enum.map(&(&1 * &1))
```

So I was thinking mor along the lines of a macro to let me assign variables at the end of the pipeline. As a bonus, you can assign variables several times. A rough sketch for such a macro is:

```
defmodule AssignToVariable do
  defmacro assign_to_variable(val, variable) do
    quote do
      var!(unquote({variable, [], nil})) = unquote(val)
    end
  end
end
```

and usage would look like this:

```
[1, 2, 3, 4]
|> assign_to_variable(:original)
|> Enum.map(&(&1 * &1))
|> assign_to_variable(:result)

IO.inspect original
IO.inspect result
```

Ben Wilson

unread,
Feb 26, 2016, 3:40:24 PM2/26/16
to elixir-lang-core
It isn't awkward, it's the entire point. Pipe's flow from top to bottom SHOULD be interrupted when you want to assign because that interruption is a visual signal to developers that something different is happening. It should interrupt any and every time you're doing something other than successively passing the output of one function as the input to the next function, because that's a different activity, and that difference matters. Good code makes that difference readily available to its readers so that they can spot the difference, it doesn't hide it.

Tallak Tveide

unread,
Feb 27, 2016, 4:27:18 AM2/27/16
to elixir-lang-core
I think should is too strong here. Perhaps it's a bad idea, but I believe at least assigning at the end of a pipeline is worth discussing.

The benefit being that you read the code in the same order that you should process it mentally. With the assignment before the pipeline, you must put that code on your 'mental stack' and remember it while reading the pipeline itself.

Actually I wanted the code to end up something like this, but lacked in macro skills and time:

```

[1, 2, 3, 4]
|> Enum.map(&(&1 * &1))
|> match_and_assign([_, x, _, y])
```

as opposed to:

 ```
[_, x, _, y] =

  [1, 2, 3, 4]
  |> Enum.map(&(&1 * &1))
```

As it stands it is quite possible to do this without a new change to the pipe operator or the Elixir language. So people might do it anyways, but we could of course discourage it..

José Valim

unread,
Feb 27, 2016, 4:45:00 AM2/27/16
to elixir-l...@googlegroups.com
I was trying to refrain from commenting but I think this goes into the opposite direction of what we are trying to do with case/cond.

Imagine a construct like shown before:

[1, 2, 3, 4]
|> assign_to_variable(:original)
|> Enum.map(&(&1 * &1))
|> assign_to_variable(:result)

Regardless of how you call the function, if I decide to move the pipeline to a separate function, I may forget the assignments in the pipeline changing the behaviour of my code. That's exactly because they don't look like an assignment in the first place. Remember that's the discussion we had with case/cond some time ago, where assignments inside the case/cond are easy to miss when moving code around.

Also, please think of the semantic overhead you are proposing. In all use cases of the match operator (=), we read it from right to left. You get what is on the right and match against what is on the left. But now folks need to understand a new construct only for the "I want to express everything with a pipeline" sake.

I will be very to the point once more: it is very unlikely we will add any extensions to the pipe operator. If you would like to see changes, build them in a separate project, convince folks they are useful so we finally get changes upstream. The whole benefit of Elixir is that you don't need to wait for the language to change for most of those features.




José Valim
Skype: jv.ptec
Founder and Director of R&D

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.

Ben Wilson

unread,
Feb 27, 2016, 7:39:21 AM2/27/16
to elixir-lang-core
Maybe this will help: just Change how you're reading it mentally. To take your original case:

Bind to result this widget mapped, filtered, and reduced.

Reply all
Reply to author
Forward
0 new messages