# Structs, Protocols and Behaviours — Elixir & Phoenix

Source: https://www.geekswithgeeks.com/en/elixir-phoenix/d-structs

> Define structs, implement protocols for polymorphism and declare behaviours.

## Structured data and polymorphism

A **struct** is a map with a fixed set of keys, defaults and a name, defined in a module with `defstruct`: `%Order{id: "o-1", total: 0}`. Structs provide compile-time key checking, pattern matching on the type (`%Order{} = value`) and `@enforce_keys` for required fields. They do not inherit the `Enum` or `Access` behaviour of maps automatically. **Protocols** provide **polymorphism over data types**: define a protocol with `defprotocol` and implement it for specific types with `defimpl`, so `Shop.Describable.describe(value)` dispatches on the value's type. Built-in protocols include **`String.Chars`** (used by interpolation), **`Inspect`**, **`Enumerable`** and **`Jason.Encoder`** in the popular JSON library. **Behaviours** define a contract for **modules**: `@callback` declarations list the functions a module must implement, and `@behaviour` plus `@impl true` mark implementations, with compiler warnings for missing callbacks. GenServer and Plug are behaviours, and behaviours are the usual way to swap implementations such as a real payment gateway and a test double.

## A struct, a protocol and a behaviour

Type-specific behaviour without inheritance.

```elixir
defmodule Shop.Order do
  @enforce_keys [:id, :customer_id]
  defstruct [:id, :customer_id, items: [], status: :placed]

  def total(%__MODULE__{items: items}), do: Enum.sum(Enum.map(items, & &1.price * &1.qty))
end

defprotocol Shop.Describable do
  @doc "A short human-readable description."
  def describe(value)
end

defimpl Shop.Describable, for: Shop.Order do
  def describe(order), do: "Order #{order.id} (#{order.status}), #{Shop.Order.total(order)} paise"
end

defimpl String.Chars, for: Shop.Order do
  def to_string(order), do: "Order<#{order.id}>"
end

defmodule Shop.PaymentGateway do
  @callback charge(order_id :: String.t(), amount :: pos_integer()) ::
              {:ok, String.t()} | {:error, atom()}
end

defmodule Shop.FakeGateway do
  @behaviour Shop.PaymentGateway

  @impl true
  def charge(order_id, amount) when amount > 0, do: {:ok, "pay_#{order_id}"}
  def charge(_order_id, _amount), do: {:error, :invalid_amount}
end

order = %Shop.Order{id: "o-1", customer_id: "c-1", items: [%{price: 4_950, qty: 2}]}
IO.puts(Shop.Describable.describe(order))
IO.puts("Created #{order}")                    # uses String.Chars
IO.inspect(Shop.FakeGateway.charge(order.id, Shop.Order.total(order)))
```

## Universal remote buttons

A protocol is a "play" button on a universal remote: each device type (TV, speaker, projector) implements what play means for it. A behaviour is a job description listing the duties any module filling that role must perform.

**Quiz:** What is the difference between a protocol and a behaviour?

- [ ] They are identical
- [ ] Behaviours dispatch on data types
- [ ] Protocols only work with strings
- [x] Protocols dispatch on the data type of a value; behaviours define functions a module must implement

*Answer:* Protocols dispatch on the data type of a value; behaviours define functions a module must implement. Protocols give data-type polymorphism; behaviours are module contracts.
