Какова цель символа "|>" в Эликсире?

Я искал документы Elixir и Phoenix, а также несколько других сайтов, таких как Изучить эликсир без везения. Вот как это выглядит:

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

Сначала я подумал, что это символ символа продолжения строки, чтобы читать код, но строка Item |> where, указанная выше, предполагает иное. Является ли это пониманием списка или чем-то, определяющим типы ввода?

Ответ 1

Я скопирую материал из моего учебного материала Elixir Express: https://github.com/chrismccord/elixir_express/blob/master/basics/06_pipeline_operator.md

Оператор трубопровода

Одной из самых простых, но эффективных функций в Elixir является оператор конвейера. Оператор конвейера решает проблему, с которой сталкиваются многие функциональные языки при составлении серии преобразований, когда выход из одной функции должен передаваться как входной сигнал другому. Это требует, чтобы решения читались в обратном порядке, чтобы понять действия, выполняемые, затрудняя читаемость и скрывая истинное намерение кода. Эликсир элегантно решает эту проблему, разрешая вывод функции, которая будет передана в качестве первого параметра на вход другого. Во время компиляции функциональная иерархия преобразуется во вложенный "обратный" вариант, который в противном случае потребовался бы.

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

Чтобы понять всю полезность, предоставляемую конвейером, рассмотрите модуль, который извлекает новые сообщения из API и сохраняет результаты в базе данных. Последовательность шагов:

  • Найти учетную запись с помощью авторизованного токена пользователя
  • Получить новые сообщения от API с авторизованной учетной записью
  • Преобразование ответа JSON на список ключевых слов
  • Сохранить все новые сообщения в базе данных

Без трубопровода:

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

Правильное именование и отступы помогают читабельности предыдущего блока, но его намерение не сразу становится очевидным, не затрачивая мгновение на разложение шагов изнутри, чтобы понять понимание потока данных.

Теперь рассмотрим этот ряд шагов с оператором конвейера:

С 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

Проводя результат каждого шага в качестве первого аргумента для следующего, позволяет писать программы в виде серии преобразований, которые любой читатель мог бы сразу прочитать и понять, не затрачивая дополнительных усилий на разворачивание функций, как в первое решение.

Стандартная библиотека Elixir фокусируется на том, чтобы помещать объект функции в качестве первого аргумента, помогая и поощряя естественное использование конвейеров.