Tracks
/
Elixir
Elixir
/
Temario
/
Comportamientos
Co

Comportamientos en Elixir

1 ejercicio

Acerca de Comportamientos

Los comportamientos nos permiten definir interfaces (conjuntos de funciones y macros) en un módulo de comportamiento que después pueden implementar distintos módulos de callback. Gracias a la interfaz compartida, esos módulos de callback se pueden usar de forma intercambiable.

Note

Fíjate en la ortografía británica de «behaviours».

Definir comportamientos

Para definir un comportamiento, tenemos que crear un módulo nuevo y especificar una lista de las funciones que forman parte de la interfaz deseada. Cada función debe definirse con el atributo de módulo @callback. La sintaxis es idéntica a la de un typespec de función (@spec). Hay que especificar el nombre de la función, una lista de tipos de argumento y todos los posibles tipos de retorno.

defmodule Countable do
  @callback count(collection :: any) :: pos_integer
end

Implementar comportamientos

Para agregar un comportamiento existente a nuestro módulo (crear un módulo de callback), usamos el atributo de módulo @behaviour. Su valor debe ser el nombre del módulo de comportamiento que estamos agregando.

Después, debemos definir todas las funciones (callbacks) que requiere ese módulo de comportamiento. Si estamos implementando el comportamiento de otra persona, como los comportamientos Access o GenServer integrados en Elixir, encontraríamos la lista de todos los callbacks del comportamiento en la documentación de hexdocs.pm.

Un módulo de callback no se limita a implementar solo las funciones que forman parte de su comportamiento. También es posible que un solo módulo implemente varios comportamientos.

Para indicar de qué comportamiento proviene cada función, debemos usar el atributo de módulo @impl antes de cada función. Su valor debe ser el nombre del módulo de comportamiento que define este callback.

defmodule BookCollection do
  @behaviour Countable

  defstruct [:list, :owner]

  @impl Countable
  def count(collection) do
    Enum.count(collection.list)
  end

  def mark_as_read(collection, book) do
    # other function unrelated to the Countable behaviour
  end
end

Implementaciones predeterminadas de callbacks

Al definir un comportamiento, es posible proporcionar una implementación predeterminada de un callback. Esta implementación debe definirse en la expresión citada de la macro __using__/1. Para que quienes usan el módulo de comportamiento puedan sobrescribir la implementación predeterminada, llama a la macro defoverridable/1 después de la implementación de la función. Acepta una lista de palabras clave con nombres de funciones como claves y aridades de funciones como valores.

defmodule Countable do
  @callback count(collection :: any) :: pos_integer

  defmacro __using__(_) do
    quote do
      @behaviour Countable
      def count(collection), do: Enum.count(collection)
      defoverridable count: 1
    end
  end
end

Ten en cuenta que no se recomienda definir funciones dentro de __using__/1 para ningún propósito que no sea definir implementaciones predeterminadas de callbacks, pero siempre puedes definir funciones en otro módulo e importarlas en la macro __using__/1.

Editar en GitHub El enlace se abre en una ventana o pestaña nueva

Aprende Comportamientos