Skip to content

Instantly share code, notes, and snippets.

@bcardarella
Created July 14, 2025 06:47
Show Gist options
  • Save bcardarella/54c31b41f64617f5b9159021b1493c6c to your computer and use it in GitHub Desktop.
Save bcardarella/54c31b41f64617f5b9159021b1493c6c to your computer and use it in GitHub Desktop.

Object

Section

defmodule Object do
  defstruct []

  defmacro __using__(fields) do
    quote do
      require Object
      Object.inherit from: Object, fields: unquote(fields)
    end
  end
  
  defmacro inherit([from: module, fields: fields]) do
    mod = Macro.expand(module, __CALLER__)
    functions =
      mod.__info__(:functions)
      |> Enum.reject(&(Atom.to_string(elem(&1, 0)) =~ "__"))
      |> Enum.map(fn({name, arity}) -> {name, build_args(arity)} end)

    fields = mod.__info__(:struct)
      |> Enum.map(&({&1.field, &1.default}))
      |> Keyword.merge(fields)
    
    delegate_calls = Enum.map(functions, fn {name, args} ->
      quote location: :keep do
        defdelegate(unquote({name, [], args}), to: unquote(__MODULE__))
      end
    end)
    
    overridable_list = Enum.map(functions, fn {name, args} -> {name, length(args)} end)
    
    quote location: :keep do
      defmacro __using__(fields) do
        quote do
          require Object
          Object.inherit from: unquote(__MODULE__), fields: unquote(fields)
        end
      end
      defstruct unquote(fields)
      unquote_splicing(delegate_calls)
      defoverridable unquote(overridable_list)
    end
  end
  
  defp build_args(arity) do
    Enum.map(1..arity, &({:"var_#{&1}", [], Elixir}))
  end
  
  def foo(bar, baz) do
    bar + baz
  end
  
  def bar(a), do: a + 2
end
{:module, Object, <<70, 79, 82, 49, 0, 0, 26, ...>>, {:bar, 1}}
Object.__info__(:macros)
[__using__: 1, inherit: 1]
defmodule Foo do
  use Object, [
    a: 1
  ]
end
{:module, Foo, <<70, 79, 82, 49, 0, 0, 14, ...>>, :ok}
defmodule Bar do
  use Foo, [
    b: 2
  ]
end
{:module, Bar, <<70, 79, 82, 49, 0, 0, 14, ...>>, :ok}
bar = %Bar{}
Bar.foo(bar.a, bar.b)
3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment