Last active
October 11, 2025 15:45
-
-
Save idugalic/bc7b9c42e170689aa8e21afbdfbf28af to your computer and use it in GitHub Desktop.
Information Systems
This file contains hidden or 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
| 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") | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is lovely! ❤️