Попытка использовать канал, но не уверен, как он извлекает предыдущее значение - PullRequest
0 голосов
/ 06 декабря 2018

Скажем, у меня сейчас что-то вроде этого:

session = conn.assigns[:session]
user = User.find_by_id(session.user_id)

Как я могу использовать |> sytle в этом scenerio?

conn.assigns[:session]
|>User.find_by_id

Как я могу пропустить предыдущие звонки и получитьсвойство user_id от него?

Также я видел людей, использующих |>, и я думаю, что они также указывали arity, как это работает?

some_code
|> some_function(&/2)

Что-то подобное, я могуне помню, но я был смущен, глядя на него.

Ответы [ 4 ]

0 голосов
/ 08 декабря 2018

Как я могу использовать стиль |> в этом сценарии?

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

Основная группа Elixir считает использование одного |> в выражении антипаттерном.Вместо этого вы должны использовать

user = User.find_by_id(conn.assigns[:session].user_id)

С другой стороны, если вы действительно хотите использовать |> здесь, вы можете использовать Access поведение:

user =
  conn.assigns
  |> Map.get(:session)
  |> Map.get(:user_id)
  |> User.find_by_id()
0 голосов
/ 06 декабря 2018

Первая часть - Вы можете использовать трубу следующим образом:

conn.assigns[:session]
|> Map.get(:user_id) # Use function from Map module.
|> User.find_by_id()

Вторая часть.Вы видите Оператор захвата

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

Давайте возьмем пример из Ecto.Changeset.validate_change / 3

, поэтому третий аргумент ожидает анонимную функцию (валидатор), которая имеет 2 аргумента, и вы можете написать приватную функциюв вашей схеме, чтобы подтвердить изменение при переходе на набор изменений и использовать его:

defp validate_title(field, title) do
  if title == "foo" do
    [{field, "cannot be foo"}]
  else
    []
  end
end

, поэтому, когда вы делаете проверку с помощью набора изменений, вы можете сделать:

changeset = change(%Post{}, %{title: "foo"})
changeset = validate_change(changeset, :title, &validate_title/2)
0 голосов
/ 07 декабря 2018

|> принимает все, что находится над ним (или слева от него), и передает значение этого выражения в качестве первого аргумента функции, которую вы вызываете с правой стороны канала.Это все, что делает труба.Итак, ваша попытка использования канала здесь:

conn.assigns[:session]
|>User.find_by_id

эквивалентна:

User.find_by_id(
    conn.assigns[:session]
) 

Вот еще один пример:

defmodule My do
  def package({x, y}) do
    %{width: x, length: y}
  end

  def modify(map, z) do
    Map.put_new(map, :height, z)
  end 

  def go(tuple) do
    tuple
    |> package()
    |> modify(10)
  end
end

В iex:

~/elixir_programs$ iex c.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> My.go {1, 2}
%{height: 10, length: 2, width: 1}

В определении My.go() переменной параметра tuple присваивается аргумент {1, 2}, что дает вам следующий конвейер:

{1, 2}
|> package()
|> modify(10)

Первый канал заставляет package()вызываться с первым аргументом {1, 2}, поэтому package({1, 2}), который возвращает

%{width: 1, length: 2}

, оставляя вас с:

%{width: 1, length: 2}
|> modify(10)

Тогда канал вызывает modify() для вызовас первым аргументом %{width: 1, length: 2}, поэтому modify(%{width: 1, length: 2}, 10), который возвращает:

%{height: 10, length: 2, width: 1}

Также я видел людей, использующих |>, и я думаю, что они также указывали arity, как эторабота?

    some_code
    |> some_function(&/2)

& используется для нескольких разных вещей.Во-первых, & создаст анонимную функцию из указанного вами выражения:

my_func = &(3*4)

Это создает анонимную функцию:

my_func = fun -> 3*4 end

Ну, не совсем.Это на самом деле не сработает, потому что & требует, чтобы выражение, следующее за &, использовало хотя бы одну переменную параметра функции.Переменная параметра функции ??!Вот второе использование &.На (неопределенные) переменные параметра для анонимной функции можно ссылаться с именами & 1, & 2, & 3 и т. Д. В выражении.Поэтому вы должны написать:

my_func = &(3 * &1)

Это создает анонимную функцию:

fn x -> 3 * x end

Анонимная функция имеет арность 1, потому что в выражении было упомянуто только &1.Если вы упомянули &1 и &2 в выражении, то арность созданной функции будет 2:

iex(5)> my_func = &(IO.puts "#{&2} ... #{&1}")
#Function<12.99386804/2 in :erl_eval.expr/5>

iex(6)> my_func.("hello", "world")
world ... hello

Если вы упомянете &1 и &3 в выражении, пропуская &2, вы получите сообщение об ошибке:

захват & 3 не может быть определен без & 2.

Вы также можете использовать & для создания анонимной функции, котораяоборачивает существующую функцию:

len = &Enum.count/1
len.([7,8,9])
=> 3

& создает анонимную функцию:

fun x -> Enum.count(x) end

Вы должны уникальным образом идентифицировать существующую функцию, указав ее имя и арность.Для локальных или импортированных функций вы можете просто написать &func_name/2 и исключить имя модуля.

0 голосов
/ 06 декабря 2018

Я думаю, что вам, вероятно, было бы лучше сопоставить шаблон с функциональной головой и извлечь user_id из назначений, сказав, что вы можете использовать Map.fetch!/2, например:

conn.assigns[:session]
|> Map.fetch!(:user_id)
|> User.find_by_id

Но, как вы видите, значение канала немного запутывается, когда вы переходите от карты к одному значению, к результату схемы.ИМО не улучшает удобочитаемость и намерения.

Вы также можете использовать conn.assigns.session.user_id |> User.find_by_id.

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

...