The game server Our game is nearly TCG (trading card game; PvP & PvE). Very complex game rule. Should view the complex rule to the users. Have many kind of cards. The calculation should be fast.
spawn_link/1 & send/2 Cons: Message passing is slow. Message queue becomes a bottleneck. Cause data copy (e.g. ETS). FP (functional programming) has no cons like this.
FP primers But they are diffent from Elixir. ML, Haskell : Have powerful type‑level cal. Lisp, Clojure : Have powerful macro & implicit state. React/Redux : Isn't immutable.
Complexity shold grow. Function grows to chaos, because a function encapsulate it's source code. Data can grow. Data is composable & extensible, because data is explicit.
Map is composable, %{a: a, b: b} == Map.merge(%{a: a}, %{b: b}) but it lacks the data about what it's made from. %{a: a, b: b} == Map.merge(%{}, %{a: a, b: b}) %{a: a, b: b} == Map.merge(%{a: a}, %{b: b}) So map isn't decomposable.
Protocol is meta‑level function that maps data to function. Protocol(T) : T -> fun(T, ...) Protocol is extensible, just like a map. Protocols ‑ Elixir https://elixir‑lang.org/getting‑ started/protocols.html Protocols ∙ Elixir School https://elixirschool.com/en/lessons/advanced/protocols/
We can list the current domain of the protocol. (Works like STI (single table inheritance) of ActiveRecord/Rails.) defprotocol P do end defmodule A do defstruct [] def type, do: "a" defimpl P do end end data = %{type: "a"} ExampleProtocol |> Protocol.extract_impls([:code.lib_dir(:example, :ebin)]) |> Enum.find(&(&1.type() == data.type)) |> struct(data)
Event sourcing Composed function is not extensible. Put a queue between functions. # Not this f2(f1(data)) data |> f1 |> f2 # This data -> queue -> f1 -> queue -> f2 a.k.a. FRP (functional reactive programming) cf. Redux, rx, clojure/core.async
Linguistic tree The slogan is “Trees arise everywhere.” :) Formal linguistics (& linguistic AI) have many patterns to treat complex data. Ex. I'm using: α‑Movement Behaviour tree GTTM (Generative Theory of Tonal Music)
Context can be composed. context_ab = new() |> put(ContextA, args) |> put(ContextB, args) context_ab = add(put(new(), ContextA, args), put(new(), ContextB, args)) This works like a decomposable map.