Arquitectura líquida. Es como el jazz — improvisáis, trabajáis juntos, tocáis los unos con los otros, haces algo, hacen algo.
—Frank Gehry
El estilo importa. Aunque Elixir tiene mucho estilo, como ocurre con todos los lenguajes, puede estropearse. No estropees el estilo.
Esta es la guía de estilo de la comunidad para el lenguaje de programación Elixir. Por favor, siéntete libre de abrir pull requests y sugerencias, y ¡forma parte de la vibrante comunidad de Elixir!
Si estás buscando otros proyectos en los que contribuir, por favor ve al sitio web de Hex package manager.
-
Usa dos espacios por nivel de indentación. No utilices tabulaciones. [enlace]
# no recomendado - cuatro espacios def some_function do do_something end # recomendado def some_function do do_something end
-
Usa los finales de línea de Unix (Los usuarios de *BSD/Solaris/Linux/OSX ya están cubiertos por defecto, pero los usuarios de Windows tendrán que prestar especial atención). [enlace]
-
Si usas Git puede que quieras utilizar la siguiente configuración para protegerte de que se te cuelen los finales de línea en Windows: [enlace]
git config --global core.autocrlf true
-
Usa espacios alrededor de operadores, después de comas, dos puntos y de punto y coma. No coloques espacios alrededor de parejas como llaves, paréntesis, etc. Los espacios en blanco puede que sean (en la mayoría de casos) irrelevantes para Elixir en tiempo de ejecución, pero su uso apropiado es clave para escribir código fácilmente legible. [enlace]
sum = 1 + 2 {a, b} = {2, 3} [first | rest] = [1, 2, 3] Enum.map(["one", <<"two">>, "three"], fn num -> IO.puts num end)
-
No utilices espacios después de operadores que no sean una palabra y que sólo reciben un argumento; o alrededor del operador de rango. [enlace]
0 - 1 == -1 ^pinned = some_func() 5 in 1..10
-
Utiliza líneas en blanco entre
def
s para separar las funciones en párrafos lógicos. [enlace]def some_function(some_data) do altered_data = Module.function(data) end def some_function do result end def some_other_function do another_result end def a_longer_function do one two three four end
-
...pero no dejes líneas en blanco cuando tengas
def
s de una sola línea cuyos argumentos representen pattern matching. [enlace]def some_function(nil), do: {:err, "No Value"} def some_function([]), do: :ok def some_function([first | rest]) do some_function(rest) end
-
Si utilizas la sintaxis
do:
con funciones y la línea que constituye el cuerpo de la función es demasiado larga, pon eldo:
en una nueva línea con un nivel de indentación más que la línea anterior. [enlace]def some_function(args), do: Enum.map(args, fn(arg) -> arg <> " is on a very long line!" end)
Si tienes varias funciones con el mismo nombre y en alguno de los casos la línea que constituye el cuerpo de la función es demasiado larga, pon el
do:
en una nueva línea para cada función:# no recomendado def some_function([]), do: :empty def some_function(_), do: :very_long_line_here # recomendado def some_function([]), do: :empty def some_function(_), do: :very_long_line_here
-
Si tienes más de un
def
s multilínea, no utilicesdef
s de una sola línea. [enlace]def some_function(nil) do {:err, "No Value"} end def some_function([]) do :ok end def some_function([first | rest]) do some_function(rest) end def some_function([first | rest], opts) do some_function(rest, opts) end
-
Usa el operador pipe (
|>
) para encadenar funciones una tras otra. [enlace]# no recomendado String.strip(String.downcase(some_string)) # recomendado some_string |> String.downcase |> String.strip # Los pipelines multilínea no se indentan a mayores some_string |> String.downcase |> String.strip # Los pipelines multilínea que estén en el lado derecho de un pattern match # deben ser indentados en una nueva línea sanitized_string = some_string |> String.downcase |> String.strip
Aunque este sea el método recomendado, ten en cuenta que al copiar y pegar pipelines multilínea en IEx podría causar un error de sintaxis, ya que IEx evaluará la primera línea sin darse cuenta de que la siguiente línea tiene otro pipeline.
-
Evita utilizar el operador pipe una única vez. [enlace]
# no recomendado some_string |> String.downcase # recomendado String.downcase(some_string)
-
Utiliza variables simples (bare variables) como comienzo de una cadena de funciones. [enlace]
# ¡NUNCA HAGAS ESTO! # Realmente se interpretará como String.strip("nope" |> String.downcase). String.strip "nope" |> String.downcase # no recomendado String.strip(some_string) |> String.downcase |> String.codepoints # recomendado some_string |> String.strip |> String.downcase |> String.codepoints
-
Al definir una lista que ocupa varias líneas, inicia la lista en una nueva línea, e indenta los elementos para mantenerlos alineados. [enlace]
# no recomendado - sin indentación list = [:first_item, :second_item, :next_item, :last_item] # mejor, pero no recomendado - con indentación list = [:first_item, :second_item, :next_item, :last_item] # recomendado - la lista comienza en su propia línea # bueno para listas cortas y compactas list = [:first_item, :second_item, :next_item, :last_item] # también recomendado - cuando cada elemento está en su propia línea # bueno para listas largas, listas con elementos largos, o listas con comentarios list = [ :first_item, :second_item, :next_item, # comentario :many_items, :last_item ]
-
Evita los espacios y tabulaciones al final de línea (trailing whitespace). [enlace]
-
Termina cada fichero con una nueva línea. [enlace]
-
Usa paréntesis cuando
def
tenga argumentos, y omítelos cuando no. [enlace]# no recomendado def some_function arg1, arg2 do # body omitted end def some_function() do # body omitted end # recomendado def some_function(arg1, arg2) do # body omitted end def some_function do # body omitted end
-
Añade una línea en blanco tras una "asignación" multilínea como una pista visual de que ha terminado. [enlace]
# no recomendado some_string = "Hello" |> String.downcase |> String.strip another_string <> some_string # recomendado some_string = "Hello" |> String.downcase |> String.strip another_string <> some_string
# tampoco recomendado something = if x == 2 do "Hi" else "Bye" end something |> String.downcase # recomendado something = if x == 2 do "Hi" else "Bye" end something |> String.downcase
-
Nunca utilices
do:
paraif/unless
multilínea. [enlace]# no recomendado if some_condition, do: # a line of code # another line of code # note no end in this block # recomendado if some_condition do # some # lines # of code end
-
Utiliza
do:
para sentenciasif/unless
de una sola línea. [enlace]# recomendado if some_condition, do: # some_stuff
-
Nunca utilices
unless
conelse
. Reescríbelo poniendo el caso positivo primero. [enlace]# no recomendado unless success? do IO.puts 'failure' else IO.puts 'success' end # recomendado if success? do IO.puts 'success' else IO.puts 'failure' end
-
Utiliza
true
como la última condición decond
cuando necesites una cláusula por defecto. [enlace]# no recomendado cond do 1 + 2 == 5 -> "Nope" 1 + 3 == 5 -> "Uh, uh" :else -> "OK" end # recomendado cond do 1 + 2 == 5 -> "Nope" 1 + 3 == 5 -> "Uh, uh" true -> "OK" end
-
Nunca dejes un espacio entre el nombre de la función y el paréntesis de apertura. [enlace]
# no recomendado f (3 + 2) + 1 # recomendado f(3 + 2) + 1
-
Utiliza paréntesis en las llamadas a funciones, sobretodo dentro de un pipeline. [enlace]
# no recomendado f 3 # recomendado f(3) # no recomendado y además se interpreta como rem(2, (3 |> g)), que no es lo que quieres. 2 |> rem 3 |> g # recomendado 2 |> rem(3) |> g
-
Omite los paréntesis en las llamadas a macros en las que se pasa un bloque do. [enlace]
# no recomendado quote(do foo end) # recomendado quote do foo end
-
Opcionalmente omite los paréntesis en llamadas a funciones (fuera de un pipeline) cuando el último argumento es una función. [enlace]
# recomendado Enum.reduce(1..10, 0, fn x, acc -> x + acc end) # también recomendado Enum.reduce 1..10, 0, fn x, acc -> x + acc end
-
Usa paréntesis para llamadas a funciones con aridad cero, de tal forma que puedan ser distinguidas de las variables. A partir de Elixir 1.4, el compilador te avisará de los lugares en los que exista ambigüedad. [enlace]
defp do_stuff, do: ... # no recomendado def my_func do do_stuff # ¿es una variable o una llamada a una función? end # recomendado def my_func do do_stuff() # esto es claramente una llamada a una función end
-
Utiliza siempre la sintaxis especial para listas de keywords. [enlace]
# no recomendado some_value = [{:a, "baz"}, {:b, "qux"}] # recomendado some_value = [a: "baz", b: "qux"]
-
Omite los corchetes de las listas de keywords siempre que sean opcionales. [enlace]
# no recomendado some_function(foo, bar, [a: "baz", b: "qux"]) # recomendado some_function(foo, bar, a: "baz", b: "qux")
-
Indenta y alinea las cláusulas
with
sucesivas. Pon el argumentodo:
en una nueva línea, indentada normalmente. [enlace]with {:ok, foo} <- fetch(opts, :foo), {:ok, bar} <- fetch(opts, :bar), do: {:ok, foo, bar}
-
Si la expresión
with
tiene un bloquedo
con más de una línea, o tiene una opciónelse
, utiliza la sintaxis multilínea. [enlace]with {:ok, foo} <- fetch(opts, :foo), {:ok, bar} <- fetch(opts, :bar) do {:ok, foo, bar} else :error -> {:error, :bad_arg} end
-
Usa
snake_case
para atoms, funciones y variables. [enlace]# no recomendado :"some atom" :SomeAtom :someAtom someVar = 5 def someFunction do ... end def SomeFunction do ... end # recomendado :some_atom some_var = 5 def some_function do ... end
-
Usa
CamelCase
para módulos (mantén los acrónimos como HTTP, RFC, XML en mayúsculas). [enlace]# no recomendado defmodule Somemodule do ... end defmodule Some_Module do ... end defmodule SomeXml do ... end # recomendado defmodule SomeModule do ... end defmodule SomeXML do ... end
-
Los nombres de macros predicado (funciones generadas en tiempo de compilación que devuelven un valor booleano) que pueden ser utilizadas dentro de guards deberían prefijarse con
is_
. Para una lista de las expresiones permitidas, échale un vistazo a la documentación de Guard. [enlace]defmacro is_cool(var) do quote do: unquote(var) == "cool" end
-
Los nombres de las funciones predicado que no pueden ser usadas dentro de guards deberían de terminar en signo de interrogación (
?
) en lugar de tener un prefijois_
(o similar). [enlace]def cool?(var) do # Complex check if var is cool not possible in a pure function. end
-
Las funciones privadas que compartan el mismo nombre con alguna función pública deben empezar con
do_
. [enlace]def sum(list), do: do_sum(list, 0) # private functions defp do_sum([], total), do: total defp do_sum([head | tail], total), do: do_sum(tail, head + total)
-
Escribe código expresivo e intenta transmitir la intención de tu programa a través de flujos de control, estructura y nombrado. [enlace]
-
Utiliza un espacio entre el carácter introductorio del comentario
#
y el resto del texto del comentario. [enlace]String.first(some_string) #no recomendado String.first(some_string) # recomendado
-
Los comentarios que sean más largos de una palabra se escribirán capitalizados, y las frases utilizarán signos de puntuación. Usa un espacio tras cada punto. [enlace]
# no recomendado # a estos comentarios en minúsculas les falta los signos de puntuación # recomendado # Ejemplo de capitalización # Usa signos de puntuación para frases completas.
-
Las anotaciones se escriben normalmente en la línea inmediatamente superior al código que anotan. [enlace]
-
La palabra clave para la anotación estará completamente en mayúsculas, seguida de dos puntos y un espacio, a continuación se añade la nota que describe el problema. [enlace]
# TODO: Deprecate in v1.5. def some_function(arg), do: {:ok, arg}
-
En casos en los que el problema sea tan obvio que cualquier tipo de documentación resultará redundante, puedes poner las anotaciones al final de la línea sin ningún tipo de nota. Este uso debería de ser la excepción y no la norma. [enlace]
start_task() Process.sleep(5000) # FIXME
-
Utiliza
TODO
para anotar código que falte o funcionalidades que deberán ser añadidas posteriormente. [enlace] -
Utiliza
FIXME
para denotar código que debe ser arreglado. [enlace] -
Utiliza
OPTIMIZE
para denotar código lento o ineficiente que pudiese llegar a causar problemas de rendimiento. [enlace] -
Utiliza
HACK
para denotar "code smells" en los que se hayan empleado prácticas de programación cuestionables y que deban ser refactorizados. [enlace] -
Utiliza
REVIEW
para denotar cualquier cosa que deba ser revisada para confirmar que funciona como se espera. Por ejemplo:REVIEW: Are we sure this is how the client does X currently?
[enlace] -
Utiliza claves de anotación propias si lo consideras oportuno, pero asegúrate de documentarlas en el fichero
README
de tu proyecto o similar. [enlace]
-
Utiliza un fichero por módulo a no ser que el módulo sea utilizado únicamente de manera interna por otro módulo (como en el caso de un test). [enlace]
-
Utiliza
snake_case
para el nombre del fichero yCamelCase
para el nombre del módulo. [enlace]# el fichero se llama some_module.ex defmodule SomeModule do end
-
Representa cada nivel de anidación dentro del módulo como un directorio. [enlace]
# el fichero se llama parser/core/xml_parser.ex defmodule Parser.Core.XMLParser do end
-
No dejes una línea en blanco tras
defmodule
. [enlace] -
Deja una línea en blanco después de cada bloque de código a nivel de módulo. [enlace]
-
Lista los atributos y directivas del módulo en el siguiente orden: [enlace]
@moduledoc
@behaviour
use
import
alias
require
defstruct
@type
@module_attribute
@callback
@macrocallback
@optional_callbacks
Añade una línea en blanco entre cada grupo, y ordena alfabéticamente los términos (como nombres de módulo). Aquí tienes un ejemplo general de cómo deberías ordenar el código en tus módulos:
defmodule MyModule do @moduledoc """ An example module """ @behaviour MyBehaviour use GenServer import Something import SomethingElse alias My.Long.Module.Name alias My.Other.Module.Example require Integer defstruct name: nil, params: [] @type params :: [{binary, binary}] @module_attribute :foo @other_attribute 100 @callback some_function(term) :: :ok | {:error, term} @macrocallback macro_name(term) :: Macro.t @optional_callbacks macro_name: 1 ... end
-
Usa la pseudo variable
__MODULE__
cuando un módulo se refiera a sí mismo. Esto evitará que tengas que actualizar cualquier referencia cuando el nombre del módulo cambie. [enlace]defmodule SomeProject.SomeModule do defstruct [:name] def name(%__MODULE__{name: name}), do: name end
-
Si prefieres utilizar otro nombre para esta referencia, define un alias. [enlace]
defmodule SomeProject.SomeModule do alias __MODULE__, as: SomeModule defstruct [:name] def name(%SomeModule{name: name}), do: name end
-
Evita repeticiones en los nombres de módulos y espacios de nombrado. Mejorará la legibilidad global y elimina alias ambiguos. [enlace]
# no recomendado defmodule Todo.Todo do ... end # recomendado defmodule Todo.Item do ... end
La documentación en Elixir (ya sea cuando es leída en iex
mediante h
o cuando es generada
con ExDoc) utiliza los atributos de módulo @moduledoc
y @doc
.
-
Incluye siempre un atributo
@moduledoc
en la línea inmediatamente posterior adefmodule
de tu módulo. [enlace]# no recomendado defmodule SomeModule do @moduledoc """ About the module """ ... end defmodule AnotherModule do use SomeModule @moduledoc """ About the module """ ... end # recomendado defmodule SomeModule do @moduledoc """ About the module """ ... end
-
Utiliza
@moduledoc false
si no pretendes documentar un módulo. [enlace]defmodule SomeModule do @moduledoc false ... end
-
Separa el código tras
@moduledoc
con una línea en blanco. [enlace]# no recomendado defmodule SomeModule do @moduledoc """ About the module """ use AnotherModule end # recomendado defmodule SomeModule do @moduledoc """ About the module """ use AnotherModule end
-
Usa heredocs con markdown para la documentación. [enlace]
# no recomendado defmodule SomeModule do @moduledoc "About the module" end defmodule SomeModule do @moduledoc """ About the module Examples: iex> SomeModule.some_function :result """ end # recomendado defmodule SomeModule do @moduledoc """ About the module ## Examples iex> SomeModule.some_function :result """ end
Typespecs es una notación para declarar tipos y especificaciones, ya sea para documentación o para la herramienta de análisis estático Dialyzer.
Los tipos propios deben de ser definidos en la parte superior del módulo junto con las demás directivas (ver Módulos).
-
Sitúa las definiciones
@typedoc
y@type
juntas, y separa cada par con una línea en blanco. [enlace]defmodule SomeModule do @moduledoc false @typedoc "The name" @type name :: atom @typedoc "The result" @type result :: {:ok, term} | {:error, term} ... end
-
Si la unión de tipos es demasiado larga para caber en una sola línea, añade una nueva línea e indenta con espacios para mantener los tipos alineados. [enlace]
# no recomendado - sin indentación @type long_union_type :: some_type | another_type | some_other_type | a_final_type # recomendado @type long_union_type :: some_type | another_type | some_other_type | a_final_type # también recomendado - un tipo por línea @type long_union_type :: some_type | another_type | some_other_type | a_final_type
-
Nombra al tipo principal para un módulo
t
, por ejemplo: la especificación de tipo para una struct. [enlace]defstruct name: nil, params: [] @type t :: %__MODULE__{ name: String.t | nil, params: Keyword.t }
-
Sitúa las especificaciones justo antes de la definición de la función, sin separarlas con una línea en blanco. [enlace]
@spec some_function(term) :: result def some_function(some_data) do {:ok, some_data} end
-
Usa una lista de atoms para los campos de la struct que tengan valor
nil
, seguida del resto de claves. [enlace]# no recomendado defstruct name: nil, params: nil, active: true # recomendado defstruct [:name, :params, active: true]
-
Omite los corchetes cuando el argumento de
defstruct
sea una lista de keywords. [enlace]# no recomendado defstruct [params: [], active: true] # recomendado defstruct params: [], active: true # obligatorio - los corchetes no son opcionales cuando la lista tenga al menos un atom defstruct [:name, params: [], active: true]
-
Indenta las líneas adicionales en la definición de una struct, manteniendo las primeras claves alineadas. [enlace]
defstruct foo: "test", bar: true, baz: false, qux: false, quux: 1
-
Haz que los nombres de las excepciones terminen en
Error
. [enlace]# no recomendado defmodule BadHTTPCode do defexception [:message] end defmodule BadHTTPCodeException do defexception [:message] end # recomendado defmodule BadHTTPCodeError do defexception [:message] end
-
Utiliza mensajes de error en minúsculas cuando lances excepciones. No utilices puntuación al final. [enlace]
# no recomendado raise ArgumentError, "This is not valid." # recomendado raise ArgumentError, "this is not valid"
Por el momento no se han añadido recomendaciones para las colecciones.
-
Haz match de strings utilizando la concatenación de string en lugar de patrones binarios: [enlace]
# no recomendado <<"my"::utf8, _rest>> = "my string" # recomendado "my" <> _rest = "my string"
Por el momento no se han añadido recomendaciones para expresiones regulares.
- Evita la metaprogramación cuando no sea necesaria. [enlace]
-
Cuando escribas aserciones con ExUnit, se consistente con el orden de los valores esperados y actuales que estás probando. Es preferible poner el valor esperado a la derecha, a no ser que la aserción sea un pattern match. [Enlace]
# recomendado - resultado esperado a la derecha assert actual_function(1) == true assert actual_function(2) == false # no recomendado - orden inconsistente assert actual_function(1) == true assert false == actual_function(2) # obligatorio - la aserción es un pattern match assert {:ok, expected} = actual_function(3)
-
Aleksei Magusev's Elixir Style Guide — Una guía que surge del estilo de programación utilizado en las librerías del core de Elixir. Desarrollada por Aleksei Magusev y Andrea Leopardi, miembros del equipo principal de Elixir. Aunque el proyecto Elixir no se adhiere a ninguna guía de estilo específica, esta es la guía más cercana a sus convenciones.
-
Credo's Elixir Style Guide — Guía de Estilo para el lenguaje Elixir, implementada para la herramienta de análisis estático de código Credo.
Dirígete a Awesome Elixir para encontrar librerías y herramientas que puedan ayudarte con el análisis de código y corrección estilo.
Es nuestra esperanza que esto se convierta en un lugar central en el que la comunidad discuta las mejores prácticas en Elixir. Estás invitado a abrir tickets y a enviar pull requests con mejoras. ¡Gracias por tu ayuda por adelantado!
Revisa la guía para contribuir y el código de conducta (ambos en inglés) para más información.
Una guía de estilo de la comunidad no tiene sentido sin el soporte de la comunidad. Por favor tuitea, ponle star, y haz que otros programadores de Elixir conozcan esta guía de forma que puedan contribuir.
Este trabajo está hecho bajo licencia Creative Commons Attribution 3.0 Unported License
La estructura de esta guía, partes del código de ejemplo, y muchos otros puntos iniciales de este documento fueron tomados de la Ruby community style guide. Muchas cosas ya eran directamente aplicables a Elixir, lo que nos permitió sacar antes este documento y empezar más rápido.
Aquí está la lista de gente que ha contribuido amablemente a este proyecto.