Skip to content

Instantly share code, notes, and snippets.

@feymartynov
Created March 12, 2019 00:50
Show Gist options
  • Save feymartynov/3bc79dffeb256beefd692c5dbadc6eee to your computer and use it in GitHub Desktop.
Save feymartynov/3bc79dffeb256beefd692c5dbadc6eee to your computer and use it in GitHub Desktop.
Encapsulation & inheritance in Elixir. This is just for fun and is not idiomatic. Please don't use it in your projects.
defmodule Inheritance do
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
@base_mod opts[:extend] || raise("`extend` option is not specified")
fields = opts[:fields] || []
fields =
if Enum.all?(fields, &is_atom/1) do
Enum.map(fields, &{&1, nil})
else
fields
end
@base_mod.__struct__()
|> Map.from_struct()
|> Enum.map(& &1)
|> Keyword.merge(fields)
|> defstruct()
# `super` is an Elixir special form so we use `base` instead
def base(%{__struct__: __MODULE__} = self, fun, args \\ []) do
keys = @base_mod.__struct__() |> Map.from_struct() |> Map.keys()
downcasted_struct = self |> Map.from_struct() |> Map.take(keys) |> @base_mod.__struct__()
apply(@base_mod, fun, [downcasted_struct | args])
end
end
end
end
defmodule Animal do
defstruct [:name]
def say(_self, _times), do: raise("Not implemented")
end
defmodule Cat do
use Inheritance, extend: Animal
def say(%__MODULE__{}, times), do: ~w(MEOW) |> List.duplicate(times) |> Enum.join(" ")
end
defmodule GrumpyCat do
use Inheritance, extend: Cat, fields: [:smile]
def say(%__MODULE__{} = self, times) do
phrase = self |> base(:say, [times])
phrase <> " " <> self.smile
end
end
defmodule Example do
def run do
cat = %Cat{name: "Simba"}
phrase = cat |> Cat.say(3)
IO.puts("#{cat.name} says: #{phrase}")
grumpy_cat = %GrumpyCat{name: "Hashish", smile: ">_<"}
phrase = grumpy_cat |> GrumpyCat.say(2)
IO.puts("#{grumpy_cat.name} says: #{phrase}")
end
end
Example.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment