What is the “|>” symbol's purpose in Elixir?

大城市里の小女人 提交于 2019-12-18 04:35:18

问题


I've searched the Elixir and Phoenix docs, as well as a few other sites like Learn Elixir with no luck. Here is what it looks like:

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 just a line continuation symbol to keep code readable, but the Item |> where line above suggests otherwise. Is it a list comprehension or something specifying input types?


回答1:


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

Pipeline Operator

One of the most simple, yet effective features in Elixir is the pipeline operator. The pipeline operator solves the issue many functional languages face when composing a series of transformations where the output from one function needs passed as the input to another. This requires solutions to be read in reverse to understand the actions being performed, hampering readability and obscuring the true intent of the code. Elixir elegantly solves this problem by allowing the output of a function to be piped as the first parameter to the input of another. At compile time, the functional hierarchy is transformed into the nested, "backward" variant 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 grasp the full utility the pipeline provides, consider a module that fetches new messages from an API and saves the results to a database. The sequence of steps would be:

  • Find the account by authorized user token
  • Fetch new messages from API with authorized account
  • Convert JSON response to keyword list of messages
  • Save all new messages to the database

Without Pipeline:

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

Proper naming and indentation help the readability of the previous block, but its intent is not immediately obvious without first taking a moment to decompose the steps from the inside out to grasp an understanding of the data flow.

Now consider this series of steps with the 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

Piping the result of each step as the first argument to the next allows allows programs to be written as a series of transformations that any reader would immediately be able to read and comprehend without expending extra effort to unwrap the functions, as in the first solution.

The Elixir standard library focuses on placing the subject of the function as the first argument, aiding and encouraging the natural use of pipelines.



来源:https://stackoverflow.com/questions/31755499/what-is-the-symbols-purpose-in-elixir

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!