Translate

domingo, 24 de noviembre de 2024

Macros en Elixir


Elixir no solo es conocido por su modelo de concurrencia y eficiencia, sino también por su poderosa capacidad de metaprogramación, gracias a sus macros. Con ellas, puedes escribir código que genera más código, permitiendo crear herramientas, DSLs (lenguajes específicos de dominio) y extender el lenguaje.

En Elixir, las macros son funciones especiales que reciben expresiones y devuelven otras expresiones transformadas. Estas transformaciones ocurren en tiempo de compilación, lo que las diferencia de las funciones normales.


defmodule EjemploMacro do

  defmacro saludo do

    quote do

      IO.puts("¡Hola, mundo!")

    end

  end

end


require EjemploMacro

EjemploMacro.saludo()

# Salida: ¡Hola, mundo!


¿Cómo funciona esto?

  • quote/2: Captura código como datos. Convierte un bloque en su representación AST (Abstract Syntax Tree).
  • unquote/1: Inserta valores dinámicos en el código generado.
  • defmacro: Declara macros.


Imagina que necesitas loguear todas las llamadas a funciones en un módulo. Con macros, puedes automatizar este proceso:


defmodule LoggerMacro do

  defmacro log_call(fun_name, do: block) do

    quote do

      IO.puts("Llamando a #{unquote(fun_name)}")

      result = unquote(block)

      IO.puts("Resultado: #{result}")

      result

    end

  end

end


defmodule Ejemplo do

  require LoggerMacro


  def calcular do

    LoggerMacro.log_call(:calcular) do

      1 + 2

    end

  end

end


Ejemplo.calcular()

# Salida:

# Llamando a calcular

# Resultado: 3


Los macros son herramientas poderosas para crear DSLs. Por ejemplo, un DSL para rutas web (similar a Plug.Router):


defmodule MiniRouter do

  defmacro route(method, path, do: block) do

    quote do

      def handle_request(unquote(method), unquote(path)) do

        unquote(block)

      end

    end

  end

end


defmodule MyRouter do

  require MiniRouter


  MiniRouter.route(:get, "/") do

    IO.puts("¡Bienvenido!")

  end


  MiniRouter.route(:post, "/create") do

    IO.puts("¡Creando recurso!")

  end

end


MyRouter.handle_request(:get, "/")

# Salida: ¡Bienvenido!


Aunque las macros son útiles, pueden complicar el código:

  • Usa macros solo si la lógica no puede resolverse con funciones.
  • Opta por composición funcional siempre que sea posible.


Las macros en Elixir son una herramienta increíblemente poderosa para extender el lenguaje y crear soluciones elegantes. Sin embargo, requieren un buen entendimiento del AST y un uso responsable.