Programming Elixir Chapter 22 Notes


Macros and Code Evaluation

My interest is in application development so macros are not something I expect to ever be creating. So this is mostly a chapter for reading.

Did work through the first two exercises. Took awhile for me to understand that I cannot use a macro-defined function in the module where it was expanded. Makes sense since Elixir first compiles then loads, so the dynamically-created function isn’t available for use.

  1. Never use a macro when you could use a function. Macros can make your code harder to understand.

  2. Macros are useful when you need to delay parameter evaluation.

  3. The require call ensures a module is compiled before the current one.

    • Watch for circular references!
  4. Macro definitions cannot be used in the file in which they are defined.

  5. When you define a new function with a macro, you cannot use that function in the same module.

MacrosAndCodeEvaluation-1

Write a macro called myunless that implements the standard unless functionality.

defmodule My do
  @moduledoc """
  My.unless true do
    "Truth"
  else
    "Falsity"
  end
  """
  defmacro unless(condition, clauses) do
    do_clause = Keyword.get(clauses, :do, nil)
    else_clause = Keyword.get(clauses, :else, nil)
    quote do
      case unquote(condition) do
        val when val in [false, nil] -> unquote(else_clause)
        _ -> unquote(do_clause)
      end
    end
  end
end
require My

My.unless 1==1 do
  "hello"
else
  "goodbye"
end

MacrosAndCodeEvaluation-2

Write a macro times_n.

defmodule Times do
  @moduledoc """
  Times.times_n(3) creates a times_3 function.
  """
  defmacro times_n(n) do
    func = String.to_atom("times_#{n}")
    quote do
      def unquote(func)(value), do: value * unquote(n)
    end
  end
end

defmodule Test do
  require Times
  Times.times_n(3)
  Times.times_n(5)
end

IO.puts Test.times_3(4)

All notes and comments are my own opinion. Follow me at @rgacote@genserver.social