Proposed syntax
[:my, :list] |> for x do
IO.puts(x)
end
Seems natural since the following forms exist:
my_var |> case do ... end
my_cond |> if do ... end
Proposed syntax
[:my, :list] |> for x do
IO.puts(x)
end
Seems natural since the following forms exist:
my_var |> case do ... end
my_cond |> if do ... end
Maybe list comprehension?
for x <- [:my, :list], do: IO.puts(x)
This can also be achieved with
[:my, :list] |> Enum.each(&IO.puts/1)
Yes, I want an alternative form for list comprehension, just like |> case do
and |> if do
are the alternative forms for case
and if
It’s probably a bit more difficult to implement than case
. How would you express
for x <- [1, 2, 3], y <- [2, 3, 4], x != y do
# ...
end
with a pipe?
And as @kokolegorille has already pointed out, simple for
cases can be expressed with one of Enum.each
, Enum.map
, or Enum.reduce
depending on the result that you want.
I do not propose and don’t have practical need for support of complex cases like you mention. It’s a shortcut anyway. Probably, there is still a reasonable syntax for that, like
[1, 2, 3] |> for x do
[2, 3, 4] |> for y, x != y, do: ...
end
A simple solution would be to leave 2nd for
in its original form:
[1, 2, 3] |> for x do
for y <- [2, 3, 4], x != y, do: ...
end
That would defeat a lot of FP rules…
[1, 2, 3] |> for x do
[2, 3, 4] |> for y, x != y, do: ...
end
that wouldn’t work, since after do
a new statement begins.
Sorry, of course that doesn’t work
Leaving the simplest form then: list |> for x do
the richer syntax probably would be list |> for x, y <- list2 do end
Pretty it ain’t.
defmodule X do # X as in dangerously eXperimental
defmacro pipe_for(enumerable, to, rest) do
quote do
unquote(
{:for, [],
[ {:<-, [], [to, enumerable]} | wrap_block(rest,[]) ]
}
)
end
end
defp wrap_block([_] = block_list, gen_filters),
do: :lists.reverse([block_list | gen_filters])
defp wrap_block([gen_filter | rest], gen_filters),
do: wrap_block(rest, [gen_filter | gen_filters])
end
defmodule Demo do
import X
def demo1() do
[:my, :list]
|> pipe_for(x, [do: (IO.puts x)])
end
def demo2() do
other = [1,2]
[:a, :b, :c]
|> pipe_for(i, [j <- other, do: ({i,j})])
end
defp multiple_of_3?(n), do: rem(n,3) == 0
def demo3() do
0..15
|> pipe_for(n, [multiple_of_3?(n), do: (n * n)])
end
def demo4(n) do
1..n
|> pipe_for(a, [
b <- 1..n,
c <- 1..n,
a + b + c <= n,
a*a + b*b == c*c,
do: (
{a, b, c}
)])
end
def demo5() do
other = [2,3,4]
[1,2,3]
|> pipe_for(x, [do: (
other
|> pipe_for(y, [x != y, do: (y)])
)])
end
end
IO.puts("-- demo1: ")
Demo.demo1()
IO.puts("-- demo 2: #{inspect Demo.demo2()}")
IO.puts("-- demo 3: #{inspect Demo.demo3()}")
IO.puts("-- demo 4: #{inspect Demo.demo4(48)}")
IO.puts("-- demo 5: #{inspect Demo.demo5()}")
$ elixir demo.exs
-- demo1:
my
list
-- demo 2: [a: 1, a: 2, b: 1, b: 2, c: 1, c: 2]
-- demo 3: [0, 9, 36, 81, 144, 225]
-- demo 4: [{3, 4, 5}, {4, 3, 5}, {5, 12, 13}, {6, 8, 10}, {8, 6, 10}, {8, 15, 17}, {9, 12, 15}, {12, 5, 13}, {12, 9, 15}, {12, 16, 20}, {15, 8, 17}, {16, 12, 20}]
-- demo 5: [[2, 3, 4], [3, 4], [2, 4]]
$
Don’t look at me, Elixir let me do it …
I just think we are putting pipes everywhere, where they aren’t needed, there is no need to put it on expressions, or even on one function without composition.
for x <- bar
is much more convenient as other languages apply the same syntax.
maybe |> case do
seems not lexical at all, case what?
myvar |> Enum.each(fn x -> x |> case do ... end end)
omg.
Soon we are in the same boat as Scala, having N ways to write the same thing.
Just don’t overuse the poor pipe.
can’t we achieve the same thing with Enum.reduce_while/3 ?
I personally don’t like it. Pipe to me says it is being inserted into the thing it points at. In this case I would expect x to be the entire list. list comprehension is much more logical to look at as for x <- [list] I read that as for x in list.