Pipe Branching Syntax Proposal

Hi,

I want to propose an extension to the language that enables it to express branched computations using the pipe |> operator. Here is a basic example of how it could be used:

def add(a, b), do: a + b
def mul(a, b), do: a * b

...

assert [4, 7, 20] ==
  2
  |> [
    add(2),
    add(5),
    mul(10)
  ]

As you see the essence would be to be able to pipe into a list with operations, and the piped value is distributed among the operations in the list, and a list is returned. The previous would be equivalent to:

assert [4, 7, 20] ==
  [
    2 |> add(2),
    2 |> add(5),
    2 |> mul(10)
  ]

I am bringing this idea from a python library I made for TensorFlow where I implement a small DSL to achieve this. You can view an example here: https://github.com/cgarciae/tensorbuilder#full-example

@cgarciae: How about this code:

operations = [&Kernel.+(&1, 2), &Kernel.+(&1, 5), &Kernel.*(&1, 10)]
Enum.map(operations, &(&1.(2)))
# [4, 7, 20]

In your case it will look like:

assert [4, 7, 20] == [&add(&1, 2), &add(&1, 5), &mul(&1, 10)]
                     |> Enum.map(operations, &(&1.(2)))
# or
def my_map(value, operations), do: Enum.map(operations, &(&1.(value))
assert [4, 7, 20] ==
  2
  |> my_map([
    &add(&1, 2),
    &add(&1, 5),
    &mul(&1, 10)
  ])

Or even like this so it is more traditionally pipe’y:

defmodule PipeMapInto do
  defmacro pipe_map_into(val_ast, applicitives_ast) when is_list(applicitives_ast) do
    do_pipe_map_into(val_ast, applicitives_ast)
  end
  defp do_pipe_map_into(val_ast, []), do: []
  defp do_pipe_map_into(val_ast, [applicitive_ast | rest_asts]) do
    [ quote do
        unquote(val_ast) |> unquote(applicitive_ast)
      end
    | do_pipe_map_into(val_ast, rest_asts)
    ]
  end
end

And used like:

iex> import PipeMapInto
PipeMapInto
iex> 2 |> pipe_map_into([
...>   add(2),
...>   add(5),
...>   mul(10),
...>   ])
[4, 7, 20]

Side Note: I’m noticing a lot of replies to old posts that have no replies, just trying to answer old posts or so? :slight_smile:

@OvermindDL1: Good macro, but it’s a little bigger code, anyway good job! :slight_smile:
Today, I have more time so I decided to at least try to answer for as much unanswered questions as possible (no matter if old or not).
btw. It could be helpful if I could sort by 2 or more columns … For example firstly sort by replies count and then sort by category.

1 Like

Yep, but it gives a more natural piping syntax. Could even change it up more too, like by another operator or something, or just parenthesis ‘sub-piping’ or so. :slight_smile:

Cool. :slight_smile:

The discourse API is pretty simple (simple JSON endpoints) so no capability sadly. We need a new Discourse, written in Elixir, that uses GraphQL instead of plain JSON. :wink:

1 Like

Could do a wrapper around the pipe too:

defmodule EnhancedPipe do
  defmacro enhanced_pipe(ast) do
    Macro.postwalk(ast, fn
      {:|>, meta, [val_ast, fun_list]} when is_list(fun_list) ->
        v = Macro.var(:v_enhanced_pipe, __MODULE__)
        fun_ast =
          quote do
            case do
              unquote(v) -> unquote(do_list_pipe(v, fun_list))
            end
          end
        {:|>, meta, [val_ast, fun_ast]}
      ast -> ast
    end)
  end
  defp do_list_pipe(val_ast, []), do: []
  defp do_list_pipe(val_ast, [applicitive_ast | rest_asts]) do
    [ quote do
        unquote(val_ast) |> unquote(applicitive_ast)
      end
    | do_list_pipe(val_ast, rest_asts)
    ]
  end
end

Used like:

iex> import EnhancedPipe
EnhancedPipe
iex> enhanced_pipe(
...> 2
...> |>[
...>   add(2),
...>   add(5),
...>   mul(10),
...>   ]
...> )
[4, 7, 20]
iex> enhanced_pipe(
...> 2
...> |>[
...>   add(2),
...>   add(5),
...>   mul(10),
...>   ]
...> |> Enum.map(&mul(&1, 2))
...> )
[8, 14, 40]

Or could just outright replace the pipe too with a macro…

1 Like