Simple and clean library that is inspired by discussion in Elixir’s issue tracker and uses operator suggested by @josevalim (however it may change, other operator that I was considering was &_)
Short example of how this works:
# Old pipes work as it have been before
iex> 5 |> Integer.to_string()
"5"
iex> 5 |> Integer.to_string(2)
"101"
# You can use `...` to mark the point where the expression should be inserted
iex> 5 |> Integer.to_string(10, ...)
"20"
For now it works only with function calls, so unfortunately no support for stuff like {:ok, ...}, [..., 2], or %{a: ...}. It also do not work with things like 1 |> struct(URI, port: ...) as the ... operator must be top level.
I wanted to create another one (the other one is @Qqwy’s CapturePipe) to test out using another operator, that would be IMHO less confusing than &1 in CapturePipe.
I’m very eager to see how this will develop. CapturePipe has two disadvantages, both related to the fact that & is already used in a somewhat different way in existing code:
There are some theoretical edge cases in which it could have surprising results. Essentially, when someone puts a pipe inside a capture, this is always turned inside-out by CapturePipe (because that’s how it works) and in certain cases this might lead to unexpected behaviour.
The formatter formats the original source code and has no clue that & will be shuffled around, so it will very much clobber the pipelines that contain it.
Magritte has neither of these disadvantages. I personally do not like using ... as chosen ‘fill in’ argument that much because it is used in many other programming languages to stand for multiple (e.g. variadic) arguments. I’d definitely be a proponent of using &_. This still does need some thought (unless a new dedicated token is added to Elixir for it) because while foo(1, &_, 3) works fine, &_ + 2 will be parsed as if it was the (nonsensical) capture &(_ + 2). Maybe there is a way to deal with this gracefully, I don’t know at this time.
I was also thinking about ^_ which would bind stronger than &_, but would be surprising behaviour once again, as it is “non standard” usage of ^ operator.
Since it’s directly related to the capture operator, i’d argue that anything who doesn’t contain & would cause more confusion, after all, it’s an “extension” of the current behavior, so it should stay on track with what it uses now. I’m not sure that i’d like &_, but at least, it keeps consistency with the overall use of this operator.
Note: I’ve clicked on ... in your poll, because i didn’t realise it would not open a page for me to select an option , but my vote goes for &_.
I like ... honestly. & is already overloaded, so sometimes I worry about adding new meanings. ... is relatively safe, doesn’t cause precedence issues, and is unlikely to be used as a variable.
To me, this is simply a consistency point of view, it really looks like using a completely different operator, with “only” the objective of extending the capabilities of what’s already there. It’s like having two different syntax to, in the end, do something quite similar to what the capture operator already does, hence why i though that using something like &_ was a better idea.
The & issues means that it’s better to just put it out of the options ?
I also put some thought into this and I’d argue that ... should always refer to the inner pipe.
But there is an argument to be made to disallow using ... in a nested pipe, similar to how nested captures (&) are disallowed. At the very least this should print a warning in my opinion.
Yeah, but & &1 + &1 is allowed, while 1 |> Kernel.+(..., ...) is not allowed in Magritte, as it would require additional variable and could cause problems with macros (for example with Logger macros which lazily evaluate arguments).
Maybe a dumb question, but if it is an operator that works with it’s left and right sides, why should it care about nesting?
I mean, a |> foo(b, ...) is reasonable, buta |> foo(fn x -> bar(x, ...) end) seems completely out of scope for the for the operator’s semantic.