How to override Kernel macros

The macro mechanism in Elixir is not only an interesting metaprogramming feature - in fact, it is at the language's very core. And the more awesome fact is that, using macros, you can override the algorithm of defining functions itself!

Table of contents

    This TIL is a build-up for Michal Buszkiewicz's ElixirConf EU 2021 talk on debugging in Elixir - see you in Warsaw on Sep 10, 2021!

    Elixir is a language built upon macros, that are used to transform code into different code. Some of the language's keywords such as case , cond , etc. are actually implemented at the compiler level and are shimmed with the Kernel.SpecialForms module, whereas others such as raise , if , unless etc. are implemented as macros that do their job interacting with Erlang-provided modules as well as those from Kernel.SpecialForms .

    Hell, even def is defined as a macro and to make things even weirder, so is defmacro itself (explained simply enough here).

    One thing that you have to keep in mind before you go further is that Kernel and Kernel.SpecialForms is imported automatically into each of your custom modules. You can, however, opt to use the except option to skip certain macros:

    defmodule NoIf do
      import Kernel, except: [if: 2]
    
      def foo() do
        # trying to use if/2 will result in an error
      end
    end

    That's just an example. How can we use it for more practical purposes? We can, for instance, override def to tamper with every function that we implement inside the module - for whatever purposes (logging function calls, other debugging purposes, etc.).

    defmodule CustomDef do
      defmacro def(call, expr \\ nil) do
        IO.inspect([call: call, expr: expr], label: "Defining")
    
        quote do
          Kernel.def(unquote(call), unquote(expr))
        end
      end
    end
    
    defmodule NoDef do
      import Kernel, except: [def: 2, def: 1, defp: 2, defp: 1]
      import CustomDef
    
      def foo(arg) do
        {:ok, :foo, arg * 2}
      end
    end
    
    IO.inspect(NoDef.foo(2), label: "Inside NoDef.foo/0")
    IO.inspect(NoDef.foo(3), label: "Inside NoDef.foo/0")
    

    The output is:

    Defining: [
      call: {:foo, [line: 15], [{:arg, [line: 15], nil}]},
      expr: [
        do: {:{}, [line: 16],
         [:ok, :foo, {:*, [line: 16], [{:arg, [line: 16], nil}, 2]}]}
      ]
    ]
    Inside NoDef.foo/0: {:ok, :foo, 4}
    Inside NoDef.foo/0: {:ok, :foo, 6}

    For demonstration purposes, I wanted to show that we were able to put an output message at the stage of defining the function, and - as you can see that expr contains the function's AST - you can alter it so that, for instance, every function you create in your module contains a logger call at the beginning.

    For easier usage in any of your modules, you could create a __using__ macro so you can plug it in with the use keyword.

    Download our ebook
    Michał Buszkiewicz, Elixir Developer
    Michał Buszkiewicz Curiosum Founder & CTO

    Read more
    on #curiosum blog

    Phoenix LiveView Tutorial: Adding Phoenix PubSub and Pow Authentication to Messenger

    We've already bootstrapped our Phoenix LiveView-based Messenger app's database structure and a first LiveView page.

    This time, we're going to improve real-time communication between the app's users using Phoenix PubSub, and use the Pow library to add secure user authentication.

    As of November 2020, the latest Phoenix LiveView version is 0.14.8 - and the series has been updated to match it!

    5 top-tier companies that use Elixir

    Elixir is a pretty capable language - and it consistently ranks near the top of most loved and wanted languages rankings. It has a large following and some very persuasive preachers as well. But that would not be enough to make me like it – what I need as real proof of its strengths is real businesses that strive with Elixir.

    That’s what this list is all about – a bunch of stories from top companies that chose Elixir and never looked back. Let us show you how its power and versatility shows in practice.