Composición
Ahora que sabemos cómo crear módulos y estructuras vamos a aprender cómo agregar la funcionalidad existente a ellos a través de la composición. Elixir nos proporciona una variedad de formas diferentes de interactuar con otros módulos.
alias
Nos permite alias nombres de módulo; utilizado con bastante frecuencia en código Elixir:
defmodule Sayings.Greetings do
def basic(name), do: "Hi, #{name}"
end
defmodule Example do
alias Sayings.Greetings
def greeting(name), do: Greetings.basic(name)
end
# Without alias
defmodule Example do
def greeting(name), do: Sayings.Greetings.basic(name)
end
Si hay un conflicto entre dos alias o simplemente deseamos alias a un nombre diferente por completo, podemos usar la opción: ":as"
defmodule Example do
alias Sayings.Greetings, as: Hi
def print_message(name), do: Hi.basic(name)
end
Incluso es posible alias múltiples módulos a la vez:
defmodule Example do
alias Sayings.{Greetings, Farewells}
end
import
Si queremos importar funciones y macros en lugar de usar alias, podemos usar import/:
iex> last([1, 2, 3])
** (CompileError) iex:9: undefined function last/1
iex> import List
nil
iex> last([1, 2, 3])
3
Filtración
De forma predeterminada, todas las funciones y macros se importan, pero podemos filtrarlas mediante las opciones :only y :except.
Para importar funciones y macros específicas, debemos especificar los pares nombre de función y numero de parámetros a: only y :except. Comencemos importando sólo la función last/1 :
iex> import List, only: [last: 1]
iex> first([1, 2, 3])
** (CompileError) iex:13: undefined function first/1
iex> last([1, 2, 3])
3
Si importamos todo excepto last/1 e intentamos usar las mismas funciones que antes:
iex> import List, except: [last: 1]
nil
iex> first([1, 2, 3])
1
iex> last([1, 2, 3])
** (CompileError) iex:3: undefined function last/1
Hay dos átomos especiales, :functions y :macros, que importan sólo funciones y macros respectivamente:
import List, only: :functions
import List, only: :macros
require
Aunque se utiliza menos frecuentemente require/2 es importante. Requerir un módulo asegura que está compilado y cargado. Esto es muy útil cuando necesitamos acceder a las macros de un módulo:
defmodule Example do
require SuperMacros
SuperMacros.do_stuff
end
Si tratamos de llamar a una macro que aún no está cargada Elixir generará un error.
use
La macro "use" invoca una macro especial, llamada __using __/1, del módulo especificado. He aquí un ejemplo:
# lib/use_import_require/use_me.ex
defmodule UseImportRequire.UseMe do
defmacro __using__(_) do
quote do
def use_test do
IO.puts "use_test"
end
end
end
end
y agregamos esta línea a UseImportRequire:
use UseImportRequire.UseMe
El uso de UseImportRequire.UseMe define una función use_test/0 mediante la invocación de la macro __using __/1.
Esto es todo lo que hace el uso. Sin embargo, es común que la macro __using__, a su vez, llame alias, require, o import. Esto a su vez creará alias o importaciones en el módulo de uso. Esto permite que el módulo que se utiliza para definir una política de cómo sus funciones y macros deben ser referenciados. Esto puede ser bastante flexible en que __using __/1 puede establecer referencias a otros módulos, especialmente submódulos.
El marco de Phoenix utiliza el use y __using __/1 para reducir la necesidad de alias repetitivas e import llamadas en módulos definidos por el usuario.
He aquí un ejemplo del módulo Ecto.Migration:
defmacro __using__(_) do
quote location: :keep do
import Ecto.Migration
@disable_ddl_transaction false
@before_compile Ecto.Migration
end
end
La macro Ecto.Migration .__ using__/1 incluye una llamada de importación para que cuando utilice Ecto.Migration también importe Ecto.Migration. También configura una propiedad de módulo que asumimos que controla el comportamiento de Ecto.
Para recapitular: la macro de uso simplemente invoca la macro __using __/1 del módulo especificado. Para entender realmente qué es lo que necesita para leer la macro __using __/1.
Dejo link:
https://elixirschool.com/en/lessons/basics/modules/