“|>”符号在 Elixir 中的用途是什么?

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

我搜索了 Elixir 和 Phoenix 文档,以及 Learn Elixir 等其他一些网站,但都没有找到。这是它的样子:

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 行表明并非如此。是列表理解还是指定输入类型的东西?

我将从我的 Elixir Express 工作室复制 material: https://github.com/chrismccord/elixir_express/blob/master/basics/06_pipeline_operator.md

管道运算符

Elixir 中最简单但最有效的功能之一是 管道运算符。管道运算符解决了许多函数式语言在组合一系列转换时面临的问题,其中一个函数的输出需要作为输入传递给另一个函数。这需要反向阅读解决方案以了解正在执行的操作,这会妨碍可读性并模糊代码的真正意图。 Elixir 优雅地解决了这个问题,它允许函数的输出 piped 作为另一个输入的第一个参数。在编译时,功能层次结构被转换为嵌套的 "backward" 变体,否则将需要这些变体。

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

正确的命名和缩进有助于前一个块的可读性,但如果不先花点时间从内到外分解步骤以掌握对数据流的理解,其意图不会立即显而易见。

现在考虑管道运算符的这一系列步骤:

有管道

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

将每个步骤的结果作为第一个参数传递给下一个允许将程序编写为一系列转换,任何 reader 都可以立即阅读和理解,而无需花费额外的努力来解包功能,如第一个解决方案。

Elixir 标准库专注于将函数的主题作为第一个参数,帮助和鼓励管道的自然使用。