hauleth

hauleth OP

Or function:

defmodule Foo do
   defp ..., do: IO.puts("Hello there")

  def run do
    ...
    IO.puts("General Kenobi")
  end
end
3
Post #11
al2o3cr

al2o3cr

Not sure if this is a bug or just a spot that needs a better error message, but using ... inside a pipe inside a fn inside a pipe fails:

iex(3)> [1,2,3] |> Enum.map(fn x -> x |> Integer.to_string(10, ...) end)

** (FunctionClauseError) no function clause matching in Magritte.locate/4    
    
    The following arguments were given to Magritte.locate/4:
    
        # 1
        nil
    
        # 2
        0
    
        # 3
        nil
    
        # 4
        []
    
    Attempted function clauses (showing 4 out of 4):
    
        defp locate([{:..., _, args} | rest], pos, nil, acc) when args == [] or not(is_list(args))
        defp locate([{:..., _, args} | _], pos, found, _acc) when args == [] or not(is_list(args))
        defp locate([arg | rest], pos, found, args)
        defp locate([], _, found, args)
    
    (magritte 0.1.1) lib/magritte.ex:91: Magritte.locate/4
    (magritte 0.1.1) lib/magritte.ex:81: Magritte.find_pos/1
    (magritte 0.1.1) lib/magritte.ex:68: Magritte.unpipe/3
    (magritte 0.1.1) lib/magritte.ex:64: Magritte.unpipe/3
    (magritte 0.1.1) lib/magritte.ex:61: Magritte.unpipe/2
    (magritte 0.1.1) expanding macro: Magritte.|>/2
    iex:3: (file)
    (magritte 0.1.1) expanding macro: Magritte.|>/2
    iex:3: (file)
0
Post #12
hauleth

hauleth OP

It wouldn’t work anyway, but I will check that out.

0
Post #13
wolf4earth

wolf4earth

Is there something which makes the top-level usage absolutely mandatory? In theory it should be possible to traverse the AST, right?

Would you be open for a PR implementing that?

0
Post #14
hauleth

hauleth OP

Nothing really, except convenience and less surprising behaviour. Because there would be question in case of something like:

foo
|> bar(fn ->
  baz(1, ...) # should this work?
end)

And if the above should work, then how about:

foo
|> bar(fn ->
  2 |> baz(1, ...) # which value should be used there?

If this should outer pipeline, then there is no way to use ... in nested pipeline, if nested then the behaviour is inconsistent.

Additionally current implementation disallows duplicated ... which would IMHO feel illogical in case of traversing whole tree.

0
Post #15
wolf4earth

wolf4earth

I see nothing wong with this.

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.

0
Post #16
hauleth

hauleth OP

Thanks, fixed on master.

This example could be reduced to the example that is almost a shame that it wasn’t a test:

x |> foo

EDIT: Released 0.1.2 that fixes this shameful bug.

0
Post #17
hauleth

hauleth OP

“Plain” examples often seems right or “nothing wrong”. Like famous C++ example

std::vector<int> v = {1,2,3};
int& ref = v[1];
v.push_back(4);
std::cout << ref;

That is the problem with such examples.

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).

0
Post #18
dorgan

dorgan

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.

0
Post #19
hauleth

hauleth OP

This is how it works right now. a |> foo(b, ...) will work, a |> foo(fn x -> bar(x, ...) end) will not. However that has another cases when it will not work, for example a |> foo(a: ...) will not work as expected.

0
Post #20