tutorial - ¿Cómo usar la palabra clave “con” en Elixir y para qué sirve?
elixir también se buscó (3)
En el elixir 1.2 han incluido la palabra clave "con", pero no me queda claro para qué sirve.
¿Cómo y en qué situación lo usaría?
En las versiones de Elixir anteriores a 1.2 cuando se usan funciones en una tubería, tendría que usar una biblioteca de mónadas o declaraciones de casos de anidamiento (que podrían ser refactorizadas usando funciones privadas, pero aún así serían verbales). with/1 permite una forma diferente de resolver este problema.
Aquí hay un ejemplo de la propuesta original :
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
Aquí está lo mismo refactorizado para usar funciones:
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
Y el mismo código usando with
:
with {:ok, binary} <- File.read(path),
{:ok, data} <- :beam_lib.chunks(binary, :abstract_code),
do: {:ok, wrap(data)}
Esto funciona porque with
solo mantendrá el encadenamiento si el valor coincide con el patrón de la izquierda. Si no, la cadena se cancela y se devuelve el primer resultado no coincidente. Por ejemplo, si el archivo no existe, File.read(path)
devolverá {:error, :enoent}
- esto no coincide {:ok, binary}
por lo que la llamada with/1
devolverá {:error, :enoent}.
Vale la pena señalar que with puede usarse con cualquier patrón, no solo {:ok, foo}
y {:error, reason}
(aunque es un caso de uso muy común).
También puede encadenar "expresiones simples", como dice el documento:
with {:ok, binary} <- File.read(path),
header = parse_header(binary),
{:ok, data} <- :beam_lib.chunks(header, :abstract_code),
do: {:ok, wrap(data)}
El header
variable estará disponible solo dentro de la sentencia with
. Más información en https://gist.github.com/josevalim/8130b19eb62706e1ab37
Una cosa para mencionar, se puede usar when
guardas with
declaración. P.ej,
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 , %{}})