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?
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).
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
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 , %{}})