# Inherit
Inherit provides pseudo-inheritance in Elixir by allowing modules to inherit struct fields, delegate function calls, and override behaviors from parent modules.
## Features
- **Struct inheritance**: Child modules inherit all fields from parent modules
- **Function delegation**: Public functions from parent modules are automatically delegated
- **Function overriding**: Parent functions marked with `defoverridable` can be overridden by child modules
- **Custom `__using__` inheritance**: Parent modules can define custom `__using__` macros that are inherited
- **Parent module access**: Use `parent()` to access the parent module and call parent functions
- **Super calls**: Use `super()` to call the original implementation of overridden functions
- **Deep inheritance chains**: Support for multiple levels of inheritance
- **GenServer integration**: Works seamlessly with GenServer and other OTP behaviors
## Usage
### Making a module inheritable
Use `Inherit` in your module and define struct fields:
```elixir
defmodule Person do
use Inherit, [
name: "",
age: 0
]
def greet(person) do
"Hello, I'm #{person.name} and I'm #{person.age} years old"
end
defoverridable greet: 1
def adult?(person) do
person.age >= 18
end
defoverridable adult?: 1 # Allow child modules to override this
def name_length(person) do
String.length(person.name)
end
# No defoverridable - child modules cannot override this
end
```
### Inheriting from a module
Use the parent module in your child module and specify additional fields:
```elixir
defmodule Employee do
use Person, [
salary: 0,
department: ""
]
# Override parent function with super call
def greet(employee) do
super(employee) <> " and I work in #{employee.department}"
end
defoverridable greet: 1
# Access parent module directly
def is_adult_person(employee) do
parent().adult?(employee)
end
# This would compile with warning but never be called:
def name_length(employee),
do: 999 # Parent didn't use defoverridable!
end
```
### Using the inherited module
```elixir
# Create an Employee struct with inherited fields
employee = %Employee{
name: "John",
age: 30,
salary: 50000,
department: "Engineering"
}
# Call overridden function (with super call)
Employee.greet(employee)
# => "Hello, I'm John and I'm 30 years old and I work in Engineering"
# Call inherited function
Employee.adult?(employee)
# => true
# Call parent function via parent()
Employee.is_adult_person(employee)
# => true
# Function without defoverridable always calls parent version
Employee.name_length(employee)
# => 4 (calls Person.name_length, not any child override)
```
## Advanced Usage
### Custom `__using__` macros
Parent modules can define their own `__using__` macros that will be inherited:
```elixir
defmodule BaseServer do
use GenServer
use Inherit, [state: %{}]
defmacro __using__(fields) do
quote do
use GenServer
require Inherit
Inherit.setup(unquote(__MODULE__), unquote(fields))
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
defwithhold start_link: 1
defoverridable start_link: 1
end
end
@impl true
def init(opts) do
{:ok, struct(__MODULE__, opts)}
end
end
defmodule MyServer do
use BaseServer, [additional_field: "value"]
# Inherits GenServer behavior and start_link function
# Can override start_link if needed
end
```
### Deep inheritance chains
```elixir
defmodule GrandParent do
use Inherit, [a: 1]
def value(x), do: x
end
defmodule Parent do
use GrandParent, [b: 2]
def value(x), do: super(x) + 10
defoverridable value: 1
end
defmodule Child do
use Parent, [c: 3]
def value(x), do: super(x) + 100
defoverridable value: 1
end
# Child.value(5) => 115 (5 + 10 + 100)
```
### Helper Functions
- `parent()` - Returns the immediate parent module
- `parent(module)` - Returns the parent of the specified module
- `super(args...)` - Calls the parent implementation when overriding inherited functions
- `defwithhold` - Prevents specified functions from being inherited by child modules
### Preventing Inheritance with `defwithhold`
By default, all public functions are inherited by child modules. Use `defwithhold` to prevent specific functions from being inherited:
```elixir
defmodule Parent do
use Inherit, [field: 1]
def inherited_function do
"This will be inherited"
end
def not_inherited_function do
"This will not be inherited"
end
defwithhold not_inherited_function: 0
end
defmodule Child do
use Parent, []
# Child.inherited_function() works automatically
# Child.not_inherited_function() raises UndefinedFunctionError
end
```
## Function Overriding Rules
**Important**: Parent modules control which functions can be overridden by child modules.
- ✅ Functions marked with `defoverridable` in the parent **CAN** be overridden by children
- ❌ Functions **NOT** marked with `defoverridable` **CANNOT** be overridden (attempts compile with warnings but never execute)
- 🔄 Child modules must also use `defoverridable` when overriding to allow further inheritance
### Example
```elixir
defmodule Parent do
use Inherit, [field: 1]
def can_override, do: "parent"
defoverridable can_override: 0
def cannot_override, do: "parent only" # No defoverridable!
end
defmodule Child do
use Parent, []
def can_override, do: "child" # ✅ Works - parent used defoverridable
defoverridable can_override: 0
def cannot_override, do: "child" # ⚠️ Compiles with warning, never called!
end
# Results:
Child.can_override() # => "child"
Child.cannot_override() # => "parent only" (parent's version always used)
```
## How it works
The inheritance system creates a tree structure where modules can inherit from parent modules and define their own functions. Here's an example inheritance tree:
```mermaid
flowchart TD
GrandParent["GrandParent<br/>use Inherit, [field: 1]<br/>defines: grandparent_func()"]
Parent["Parent<br/>use GrandParent, [field: 2]<br/>inherits: grandparent_func()<br/>defines: parent_func()"]
Uncle["Uncle<br/>use GrandParent, [field: 3]<br/>inherits: grandparent_func()<br/>defines: uncle_func()"]
Child["Child<br/>use Parent, [field: 4]<br/>inherits: grandparent_func(), parent_func()<br/>defines: child_func()"]
GrandParent --> Parent
GrandParent --> Uncle
Parent --> Child
style GrandParent fill:#FF9800,stroke:#E65100,stroke-width:3px,color:#fff
style Parent fill:#2196F3,stroke:#0D47A1,stroke-width:3px,color:#fff
style Uncle fill:#4CAF50,stroke:#1B5E20,stroke-width:3px,color:#fff
style Child fill:#9C27B0,stroke:#4A148C,stroke-width:3px,color:#fff
```
**Inheritance tree explanation:**
- **GrandParent**: Root module that uses `Inherit` and defines `grandparent_func()`
- **Parent**: Inherits from GrandParent, gets `grandparent_func()` automatically, defines `parent_func()`
- **Uncle**: Also inherits from GrandParent (sibling to Parent), gets `grandparent_func()`, defines `uncle_func()`
- **Child**: Inherits from Parent, gets both `grandparent_func()` and `parent_func()` automatically, defines `child_func()`
**Function availability:**
```elixir
# Child has access to all functions in the inheritance chain
Child.grandparent_func() # Delegated from GrandParent
Child.parent_func() # Delegated from Parent
Child.child_func() # Defined locally
# Uncle only has access to GrandParent functions
Uncle.grandparent_func() # Delegated from GrandParent
Uncle.uncle_func() # Defined locally
# Parent has access to GrandParent functions
Parent.grandparent_func() # Delegated from GrandParent
Parent.parent_func() # Defined locally
```
**Key inheritance principles:**
- **Deep inheritance**: Child inherits transitively through the entire chain (GrandParent → Parent → Child)
- **Sibling inheritance**: Uncle and Parent both inherit from GrandParent but are independent of each other
- **Function delegation**: All ancestor functions are automatically available through delegation
- **Custom behavior**: Each module can define its own functions while inheriting from ancestors
This ensures that:
- All ancestor functions are available through delegation
- Custom `__using__` macros from ancestors are inherited
- Direct parent `__using__` is not duplicated
- The inheritance chain is properly established
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `inherit` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:inherit, "~> 0.1.0"}
]
end
```
## Documentation
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/inherit>.