Last active
December 24, 2024 05:20
-
-
Save spacebat/a3056267e757b97cfd63d9cfcb51e624 to your computer and use it in GitHub Desktop.
GenSimpl demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# GenSimpl will wrap the public functions of a state management module for a GenServer | |
# This state management module can be tested simply with a focus on the the internal logic of pure functions | |
defmodule MyStack do | |
defstruct stack: [] | |
def new(initial_elements \\ []), do: struct!(__MODULE__, stack: initial_elements) | |
def pop(%{stack: []}), do: :error | |
def pop(%{stack: [head | tail]} = state), do: {head, %{state | stack: tail}} | |
def push(%{stack: stack} = state, value), do: {:ok, %{state | stack: [value | stack]}} | |
def get_state(%{stack: stack} = state), do: {stack, state} | |
end | |
# This GenServer has pop/1, push/2 and get_state/1 available in its API, as implemented above and demoed below. | |
# It can be tested separately with a focus on its behaviour as a server. | |
defmodule MyStackServer do | |
use GenServer | |
use GenSimpl, from: MyStack, except: [:new] | |
def start(stack \\ []), do: GenServer.start(__MODULE__, stack) | |
@impl GenServer | |
def init(init_arg), do: {:ok, MyStack.new(init_arg)} | |
end | |
# iex> {:ok, stack} = MyStackServer.start([1,3]) | |
# {:ok, #PID<0.1678.0>} | |
# iex> MyStackServer.get_state(stack) | |
# [1, 3] | |
# iex> MyStackServer.pop(stack) | |
# 1 | |
# iex> MyStackServer.pop(stack) | |
# 3 | |
# iex> MyStackServer.get_state(stack) | |
# [] | |
# iex> MyStackServer.push(stack, 17) | |
# :ok | |
# iex> MyStackServer.get_state(stack) | |
# [17] | |
# If I add debug?: true to the end of the use GenSimpl line, then compilation of the MyStackServer module prints | |
# the following, with the actual code generated at the end labeled AST: | |
# Compiling 1 file (.ex) | |
# only: %{ | |
# module_info: %{0 => true, 1 => true}, | |
# new: %{0 => true, 1 => true}, | |
# get_state: %{1 => true}, | |
# push: %{2 => true}, | |
# __info__: %{1 => true}, | |
# __struct__: %{0 => true, 1 => true}, | |
# pop: %{1 => true} | |
# } | |
# only: %{module_info: %{*: true}, new: %{*: true}} | |
# except: %{module_info: %{*: true}, new: %{*: true}} | |
# except: %{module_info: %{*: true}, new: %{*: true}} | |
# functions_post_only: [ | |
# __info__: 1, | |
# __struct__: 0, | |
# __struct__: 1, | |
# get_state: 1, | |
# new: 0, | |
# new: 1, | |
# pop: 1, | |
# push: 2, | |
# module_info: 0, | |
# module_info: 1 | |
# ] | |
# functions_post_underscore: [get_state: 1, new: 0, new: 1, pop: 1, push: 2, module_info: 0, module_info: 1] | |
# functions_post_except: [get_state: 1, pop: 1, push: 2] | |
# AST: | |
# [ | |
# def get_state(server \\ __MODULE__) do | |
# GenServer.call(server, {&MyStack.get_state/1, []}) | |
# end, | |
# def pop(server \\ __MODULE__) do | |
# GenServer.call(server, {&MyStack.pop/1, []}) | |
# end, | |
# def push(server \\ __MODULE__, arg1) do | |
# GenServer.call(server, {&MyStack.push/2, [arg1]}) | |
# end, | |
# def handle_call({impl_function, args}, _from, state) when is_function(impl_function) do | |
# GenSimpl.apply_call(impl_function, state, args) | |
# end | |
# ] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment