Hello,
I’m excited to introduce dsm, a zero-dependency abstraction library that allows you to define Finite State Machines and pipelines in a declarative manner.
Why?
I was building a networking app that dealt with enveloped messages. Upon receiving a message the app has to unwrap the message, read the type of the message, decode the message and then process accordingly. There were about 15 different message types and I ended up writing 15 case statements. While the code looked good, I yearned for a better way to describe the transformations and the processing sequence. dsm was built for this.
How is this different from other FSM libraries?
All FSMs and associated FSM libraries expect an input event which tiggers a state transition.
I’ve observed that many libraries expect this event to be defined while defining the state machine. My issue with this was that I couldn’t define an event at compile time, instead I wanted to define functions that decide whether the input event is valid and a state transition can be triggered.
An example dsm definition to read and parse a .wav file would look like the following snippet
defmodule Example.Demo do
import Example.WaveFormat
import Example.Validations
use Dsm
dsm do
[
name: "wav-file-reader-state-machine",
initial_state: :ready,
all_states: [:ready]
]
end
dsm_state :ready do
[
transformations: [&read_file_to_bytes/1],
msg_handler_pipeline: [
{&valid_read_output?/1, &read_riff_chunk/1},
{&valid_intermediate_output?/1, &read_fmt_subChunk/1},
{&valid_intermediate_output?/1, &read_data_subChunk/1}
],
error_handlers: [],
telemetry: &__MODULE__.telemetry/2
]
end
def telemetry({state, m, f, a}, data) do
IO.inspect({"Telemetry Data for state :#{state} -- #{m}.#{f}/#{a}", data})
end
end
I would very much appreciate it if you could use dsm and provide any feedback you may have on this.
The components and structure are documented in detail in my Github and on Hexdocs.
Thanks!
Hex url: dsm | Hex
Github:






















