Skip to content

Instantly share code, notes, and snippets.

@idugalic
Last active October 11, 2025 15:45
Show Gist options
  • Select an option

  • Save idugalic/bc7b9c42e170689aa8e21afbdfbf28af to your computer and use it in GitHub Desktop.

Select an option

Save idugalic/bc7b9c42e170689aa8e21afbdfbf28af to your computer and use it in GitHub Desktop.
Information Systems
package com.fraktalio
// ###########################################################################
// ########### Information Systems encoded in Kotlin's type system ###########
// #### (encoding information systems as composable algebraic structures) ####
// ###########################################################################
/**
* Represents a **traditional (state-stored) information system**.
*
* In this model, only the *current state* is stored — previous states are
* overwritten. The system transitions from one state to another
* when a command (an intent to change state) is applied.
*
* Formally:
* ```
* Command × State → State
* ```
*
* @param Command The intent to modify the system.
* @param State The current system state.
*/
typealias StateStoredSystem<Command, State> = (Command, State) -> State
/**
* Represents an **event-sourced information system**.
*
* In this model, state is derived from a *sequence of events* rather than being
* stored directly. Each new command produces additional events, which are
* appended immutably to the event log.
*
* Formally:
* ```
* Command × Sequence<Event> → Sequence<Event>
* ```
*
* @param Command The intent to modify the system.
* @param Event A recorded state transition (an immutable fact).
*/
typealias EventSourcedSystem<Command, Event> = (Command, Sequence<Event>) -> Sequence<Event>
/**
* A **generalized model** that captures both state-stored and event-sourced systems.
*
* A system is defined by three pure functions:
* 1. [decide]: Determines which events should occur given a command and current state.
* 2. [evolve]: Evolves the state by applying an event.
* 3. [initialState]: Returns the system’s initial state.
*
* Together, these form a reusable and composable algebra for information systems.
*
* @param Command The intent to modify the system.
* @param State The state representation.
* @param Event The domain events that represent state transitions.
*/
data class System<Command, State, Event>(
val decide: (Command, State) -> Sequence<Event>,
val evolve: (State, Event) -> State,
val initialState: () -> State
)
// ALTERNATIVE:
// typealias System<Command, State, Event> = Triple<
// (Command, State) -> Sequence<Event>, // 1. decide
// (State, Event) -> State, // 2. evolve
// () -> State // 3. initialState
// >
/**
* Converts a general [System] into a traditional [StateStoredSystem].
*
* The resulting system computes the new state directly by applying all
* decided events to the initial state.
*/
fun <Command, State, Event> System<Command, State, Event>.asStateStoredSystem(): StateStoredSystem<Command, State> =
{ command, state ->
val start = state ?: initialState()
decide(command, start).fold(start) { acc, event -> evolve(acc, event) }
}
/**
* Converts a general [System] into an [EventSourcedSystem].
*
* The resulting system computes new events given a command and the existing
* event history.
*/
fun <Command, State, Event> System<Command, State, Event>.asEventSourcedSystem(): EventSourcedSystem<Command, Event> =
{ command, events ->
decide(command, events.fold(initialState()) { acc, event -> evolve(acc, event) })
}
// #############################################################
// ## Functor-like mappings ##
// #############################################################
// These transformations allow a System to adapt to new types
// without changing its internal behavior.
// They enable:
// - ✅ Type safety (checked at compile time)
// - 🔁 Composability (systems can be combined)
// - ⚙️ Flexibility (easily adapt existing systems)
// #############################################################
/**
* Transforms the command type of a system.
*
* Allows adapting a system that handles commands of type [Command]
* to one that accepts commands of type [Command2].
*
* @param f A mapping function from Command2 → Command.
*/
inline fun <Command, Command2, State, Event> System<Command, State, Event>.mapCommand(
crossinline f: (Command2) -> Command
): System<Command2, State, Event> = System(
decide = { command2, state -> decide(f(command2), state) }, evolve = evolve, initialState = initialState
)
/**
* Transforms the event type of a system.
*
* Allows interoperation between systems that emit or consume different event
* representations, by providing conversion functions between them.
*
* @param fl A function mapping external Event2 → internal Event.
* @param fr A function mapping internal Event → external Event2.
*/
inline fun <Command, State, Event, Event2> System<Command, State, Event>.mapEvent(
crossinline fl: (Event2) -> Event, crossinline fr: (Event) -> Event2
): System<Command, State, Event2> = System(
decide = { command, state -> decide(command, state).map { fr(it) } },
evolve = { state, event2 -> evolve(state, fl(event2)) },
initialState = initialState
)
/**
* Transforms the state type of a system.
*
* Enables adapting a system to a different internal representation of state
* while preserving its behavior.
*
* @param fl A function mapping external State2 → internal State.
* @param fr A function mapping internal State → external State2.
*/
inline fun <Command, State, State2, Event> System<Command, State, Event>.mapState(
crossinline fl: (State2) -> State, crossinline fr: (State) -> State2
): System<Command, State2, Event> = System(
decide = { command, state2 -> decide(command, fl(state2)) },
evolve = { state2, event -> fr(evolve(fl(state2), event)) },
initialState = { fr(initialState()) })
fun main() {
println("Learn more at: https://fmodel.fraktalio.com")
}
@ismasan
Copy link

ismasan commented Apr 29, 2025

This is lovely! ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment