Rockwell Schrock

Home // Posts // Projects // About

Abusing Elixir: classes

March 5, 2023

Is immutability dragging you down? Despite what the docs claim, Elixir isn't a real functional language. We can mutate state whenever we want!

defmodule Car do
  defstruct [:gear, :rpm, :speed, :shift, :dealloc]

  # Constructor
  def new do
    state = %{
      gear: 1,
      rpm: 1000

    {:ok, agent} = Agent.start(fn -> state end)

      # Properties
      gear: fn -> Agent.get(agent, & &1.gear) end,
      rpm: fn -> Agent.get(agent, & &1.rpm) end,
      # Read-only property
      speed: fn -> Agent.get(agent, &(&1.gear * &1.rpm)) end,
      # Method
      shift: fn diff ->
        Agent.update(agent, fn state ->
          Map.update!(state, :gear, &(&1 + diff))
      # Destructor
      dealloc: fn -> Agent.stop(agent) end

And, in "use":

iex(1)> car =
  gear: #Function<3.34229322/0 in>,
  rpm: #Function<4.34229322/0 in>,
  shift: #Function<2.34229322/1 in>,
  speed: #Function<5.34229322/0 in>,
  dealloc: #Function<6.34229322/0 in>
iex(2)> car.gear.()
iex(3)> car.shift.(1)
iex(4)> car.gear.()
iex(5)> car.speed.()
iex(6)> car.dealloc.()
iex(7)> car.gear.()
** (exit) exited in:<0.189.0>, {:get, #Function<9.34229322/1 in>}, 5000)
    ** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
    (elixir 1.14.0) lib/gen_server.ex:1038: