MVU/TEA Implementation Research

Summary

Research on Model-View-Update (MVU) / The Elm Architecture (TEA) implementations across 15+ frameworks and languages, focusing on initialization patterns.

Complete List of MVU/TEA Implementations

1. Elm (Original - JavaScript/Web)

2. Bubble Tea (Go/TUI)

3. TCA (The Composable Architecture) (Swift)

4. Flutter Bloc (Dart/Flutter)

5. Android MVI (Kotlin/Android)

6. Redux (JavaScript/React)

7. Iced (Rust/GUI)

8. Elmish (F#/Fable)

9. Hyperapp (JavaScript/Web)

10. Meiosis (JavaScript/Web)

11. SAM Pattern (JavaScript)

12. Fabulous (F#/MAUI)

13. BlazorMVU (.NET/Blazor)

14. MauiReactor (.NET/MAUI)

15. MVUX (.NET)

16. ngx-mvu (Angular MVU) (TypeScript/Angular)

Initialization Patterns Analysis

Pattern 1: Tuple Return (Most Common)

Frameworks: Elm, Bubble Tea, Iced, Elmish, Hyperapp (array variant)

init : Flags -> (Model, Command)

Characteristics: - Returns both initial state AND initial effect/command - Highly composable (can combine child inits) - Flags/context parameter for runtime initialization

Example (Iced - Rust):

fn new(flags: Flags) -> (MyApp, Command<Message>) {
  let initial_state = MyApp { count: flags.initial_count };
  let initial_cmd = Command::none();
  (initial_state, initial_cmd)
}

Pattern 2: Method on Model

Frameworks: Bubble Tea

func (m Model) Init() tea.Cmd {
  return fetchDataCmd()
}

Characteristics: - Model constructed first, then Init() called - Only returns command, state already set - Less composable (harder to combine states)

Pattern 3: Constructor/Factory

Frameworks: Flutter Bloc, Android MVI, Redux, TCA

MyBloc() : super(InitialState()) {
  // optional: dispatch initial events
}

Characteristics: - Initial state in constructor parameter - Effects/commands dispatched separately (if at all) - OOP-style initialization

Pattern 4: Property/Config Object

Frameworks: Hyperapp, Meiosis

app({
  init: { count: 0 },  // or [state, effect1, effect2]
  view: ...,
  update: ...
})

Characteristics: - Declarative initialization - Can combine state + effects in array format - Very flexible

Key Findings for Rooibos

βœ… Flags/Props Pattern is Proven

Iced (Rust) explicitly uses a flags parameter in new():

fn new(flags: Flags) -> (State, Command) { ... }

This directly supports your proposal for: - Root fragments: Init.(argv:, env:) - Child fragments: Init.(theme: :dark, debug: true)

βœ… Tuple Return (Model, Command) is Standard

Almost all functional MVU implementations return (state, command): - Elm: (Model, Cmd Msg) - Iced: (State, Command<Message>) - Elmish: (Model, Cmd<Msg>) - Hyperapp: [state, ...effects] (array variant)

Your proposal aligns perfectly: Init = ->(flags) { [model, command] }

βœ… DWIM Return Values

Hyperapp allows both: - init: state (no command) - init: [state, cmd1, cmd2] (with effects)

This supports your DWIM proposal:

Init = -> { Model.new(...) }                  # Just model
Init = -> { [Model.new(...), some_cmd] }      # With command

⚠️ Composition is Critical

The OOP frameworks (Bloc, MVI, TCA) struggle with composition: - Hard to combine child states in parent’s init - Usually rely on dependency injection or external configuration

Functional MVU frameworks excel here:

-- Elm example
init flags =
  let
    (childModel1, childCmd1) = Child1.init flags.theme
    (childModel2, childCmd2) = Child2.init flags.debug
  in
    ( { child1 = childModel1, child2 = childModel2 }
    , Cmd.batch [Cmd.map Child1Msg childCmd1, Cmd.map Child2Msg childCmd2]
    )

Your proposal enables this exact pattern in Ruby!

πŸ“Š No Framework Uses Static Constants

CRITICAL: Not a single MVU framework uses a static constant for initial state in the way we currently do with INITIAL or MODEL.

All use one of: 1. Callable with flags (Elm, Iced, Elmish) 2 Method on instance (Bubble Tea) 3. Constructor parameter (Bloc, MVI, TCA) 4. Config property (Hyperapp, Meiosis)

The static constant pattern appears to be unique to our current implementation and is unsupported by the broader MVU ecosystem.

Recommendations

1. Adopt Init Callable with Flags

Most aligned with functional MVU tradition (Elm, Iced, Elmish).

Init = ->(theme: :dark, env: {}) do
  [Model.new(theme: theme), nil]
end

2. Support Tuple Return

Follow Elm/Iced pattern: [model, command]

3. Enable DWIM

Like Hyperapp, support both: - Model.new(...) (no command) - [Model.new(...), cmd] (with command)

4. Fractal Composition Example

From Elm/Iced patterns:

module Dashboard
  Init = ->(theme: :dark) do
    stats_model, stats_cmd = StatsPanel::Init.(theme: theme)
    network_model, network_cmd = NetworkPanel::Init.(theme: theme)

    model = Model.new(stats: stats_model, network: network_model)
    command = Command.batch(
            Rooibos.route(stats_cmd, :stats),
            Rooibos.route(network_cmd, :network)
    )

    [model, command]
  end
end

5. Runtime Integration

From Iced/Elm patterns:

Rooibos.run(
        fragment: App,
        argv: ARGV,
        env: ENV
)
# Internally calls: model, cmd = App::Init.(argv: ARGV, env: ENV)

Conclusion

Your Init callable proposal is strongly validated by existing MVU/TEA implementations:

  1. βœ… Flags/props for parameterization (Iced, Elm)

  2. βœ… Tuple return of (model, command) (Elm, Iced, Elmish)

  3. βœ… DWIM flexibility (Hyperapp)

  4. βœ… Composition-first (all functional MVU)

  5. ❌ Static constants are not used by any major framework

The pattern is battle-tested across 15+ implementations in production systems ranging from web apps to mobile to desktop GUIs.