" character in Elixir? I searched Elixir and Phoenix docs, as well as several other sites like Learn Elixir with ...">

What is the purpose of the "|>" character in Elixir?

I searched Elixir and Phoenix docs, as well as several other sites like Learn Elixir with No Luck. This is how it looks:

defp update_positions(item_ids) do
  item_ids = String.split(item_ids, ",")
                    |> Enum.map fn item_id -> String.to_integer(item_id) end

  items = Repo.all(Item |> where([item], item.id in array(^item_ids, :integer)))
  item_hash = Enum.reduce items, %{}, fn item, map -> Map.put(map, item.id, item) end

  item_ids
    |> Stream.with_index
    |> Enum.each fn {item_id, index} ->
      item = item_hash[item_id]
      Repo.update(%{item | position: index + 1})
    end
end

      

At first I thought it was a line continuation character to read the code, but the line Item |> where

above suggests otherwise. Is it list comprehension or something defining input types?

+13


source to share


1 answer


I'll copy stuff from my Elixir Express tutorial material: https://github.com/chrismccord/elixir_express/blob/master/basics/06_pipeline_operator.md

Pipeline operator

One of the simplest yet most powerful features in Elixir is the pipeline operator. The pipeline operator solves a problem that many functional languages ​​face when composing a series of transformations, where the output from one function must be passed as input to another. This requires that solutions be read in reverse order to understand the actions being taken, making it difficult to read and obscuring the true intent of the code. Elixir elegantly solves this problem by allowing the output of a function to be passed as the first parameter to the input of another. At compile time, the functional hierarchy is converted to a nested "reverse" option that would otherwise be required.

iex(1)> "Hello" |> IO.puts
Hello
:ok
iex(2)> [3, 6, 9] |> Enum.map(fn x -> x * 2 end) |> Enum.at(2)
18

      

To understand the usefulness of the pipeline, consider a module that retrieves new messages from an API and stores the results in a database. Sequence of steps:

  • Find an account using an authorized user token
  • Get new messages from API with an authorized account
  • Convert JSON response to a list of keywords
  • Save all new messages to the database

Without piping:



defmodule MessageService do
  ...
  def import_new_messages(user_token) do
    Enum.each(
      parse_json_to_message_list(
        fetch(find_user_by_token(user_token), "/messages/unread")
    ), &save_message(&1))
  end
  ...
end

      

Correct naming and indentation helps the readability of the previous block, but its intention is not immediately apparent without taking a moment to break down the steps from the inside out to understand the understanding of the data flow.

Now let's look at this series of steps with a pipeline operator:

With Pipeline

defmodule MessageService do
  ...
  def import_new_messages(user_token) do
    user_token
    |> find_user_by_token
    |> fetch("/messages/unread")
    |> parse_json_to_message_list
    |> Enum.each(&save_message(&1))
  end
    ...
end

      

By passing the result of each step as the first argument to the next, it allows you to write programs as a series of transformations that any reader could read and understand right away without spending extra effort on expanding functions, as in the first solution.

The Elixir Standard Library focuses on putting a function object as the first argument, helping and encouraging the natural use of pipelines.

+26


source







All Articles