web-dev-qa-db-fra.com

Comment utiliser le mot-clé "avec" dans Elixir et à quoi sert-il?

Dans elixir 1.2, ils ont inclus le mot-clé "avec", mais je ne sais pas trop à quoi il sert.

Comment et dans quelle situation devrais-je l'utiliser?

45
diogovk

Dans les versions d'Elixir antérieures à 1.2 lors de l'utilisation de fonctions dans un pipeline, vous devrez soit utiliser une bibliothèque monade soit des instructions de casse imbriquées (qui pourraient être refactorisées à l'aide de fonctions privées, mais finiraient par être verbeuses). avec/1 permet une manière différente de résoudre ce problème.

Voici un exemple de la proposition originale :

case File.read(path) do
  {:ok, binary} ->
    case :beam_lib.chunks(binary, :abstract_code) do
      {:ok, data} ->
        {:ok, wrap(data)}
      error ->
        error
    end
  error ->
    error
end

Voici la même chose refactorisée pour utiliser les fonctions:

path
|> File.read()
|> read_chunks()
|> wrap()

defp read_chunks({:ok, binary}) do
  {:ok, :beam_lib.chunks(binary, :abstract_code)}
end
defp read_chunks(error), do: error

defp wrap({:ok, data}) do
  {:ok, wrap(data)}
end
defp wrap(error), do: error

Et le même code en utilisant with:

with {:ok, binary} <- File.read(path),
     {:ok, data} <- :beam_lib.chunks(binary, :abstract_code),
     do: {:ok, wrap(data)}

Cela fonctionne car with ne continuera de chaîner que si la valeur correspond au modèle de gauche. Sinon, la chaîne est abandonnée et le premier résultat non correspondant est renvoyé. Par exemple, si le fichier n'existe pas, File.read(path) renverra {:error, :enoent} - cela ne correspond pas à {:ok, binary} Donc l'appel with/1 Renverra {:error, :enoent}.

Il convient de noter que with peut être utilisé avec n'importe quel modèle, pas seulement {:ok, foo} Et {:error, reason} (Bien qu'il s'agisse d'un cas d'utilisation très courant).

68
Gazler

Vous pouvez également chaîner des "expressions nues", comme le dit le doc:

with {:ok, binary} <- File.read(path),
     header = parse_header(binary),
     {:ok, data} <- :beam_lib.chunks(header, :abstract_code),
     do: {:ok, wrap(data)}

La variable header sera disponible uniquement à l'intérieur de l'instruction with. Plus d'informations sur https://Gist.github.com/josevalim/8130b19eb62706e1ab37

16
leandrocp

Une chose à mentionner, vous pouvez utiliser when guard dans l'instruction with. Par exemple,

defmodule Test do
  def test(res) do
    with {:ok, decode_res} when is_map(decode_res) <- res
    do
      IO.inspect "ok"
    else
      decode_res when is_map(decode_res) -> IO.inspect decode_res
      _ ->
        IO.inspect "error"
    end
  end
end
Test.test({:ok , nil})
Test.test({:ok , 12})
Test.test({:ok , %{}})
3
YongHao Hu