### Counter Component Example (Elm) Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Illustrates a stateful counter component in Elm, following the Elm Architecture. It defines separate initial model, transition function, and view calculations, and shows how to use Browser.sandbox for application setup. ```Elm module Main exposing (main) import Browser import Counter update = Counter.update 1 view = Counter.view 1 "counter" main = Browser.sandbox { init = Counter.init, update = update, view = view } ``` -------------------------------- ### Counter Component Example (Bonsai) Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Demonstrates a stateful counter component using Bonsai's state_machine1 primitive. It shows how to create a component that yields both a view and a counter value, and how to integrate it into an application. ```Bonsai open! Core open! Import let app graph = let view, _ = Counter.component ~label:(Bonsai.return "counter") graph in view ;; let () = Start.start app ``` -------------------------------- ### Parallel Composition Example (Bonsai) Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Demonstrates parallel composition of isolated components in Bonsai. Two independent counter components are created and arranged side-by-side within a container. ```Bonsai open! Core open! Import let app graph = let first, _ = Counter.component ~label:(Bonsai.return "first") graph in let second, _ = Counter.component ~label:(Bonsai.return "second") graph in let%map first and second in N.div [ first; second ] ;; let () = Start.start app ``` -------------------------------- ### Bonsai Parallel Composition Example Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md This OCaml code demonstrates parallel composition using Bonsai. It creates two independent Counter components and composes their views. ```ocaml open! Core open! Import let app graph = let first, _ = Counter.component ~label:(Bonsai.return "first") graph in let second, _ = Counter.component ~label:(Bonsai.return "second") graph in let%map first and second in N.div [ first; second ] ;; let () = Start.start app ``` -------------------------------- ### Parallel Composition Example (Elm) Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Shows parallel composition of components in Elm. This snippet sets up the structure for composing multiple independent components, though the specific rendering of the second component is omitted in the provided text. ```Elm module Main exposing (main) import Browser import Counter ``` -------------------------------- ### Elm Parallel Composition Example Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md This Elm code showcases parallel composition by creating two Counter components. It defines the necessary Model, Msg, init, update, and view functions to manage and render these components. ```elm module Main exposing (main) import Browser import Counter import Html exposing (Html, div) type alias Model = { first : Counter.Model, second : Counter.Model } init : Model init = { first = Counter.init, second = Counter.init } type Msg = First Counter.Msg | Second Counter.Msg update : Msg -> Model -> Model update msg model = case msg of First msg_first -> { model | first = Counter.update 1 msg_first model.first } Second msg_second -> { model | second = Counter.update 1 msg_second model.second } view : Model -> Html Msg view model = div [] [ Html.map First (Counter.view 1 "first" model.first) , Html.map Second (Counter.view 1 "second" model.second) ] main = Browser.sandbox { init = init, update = update, view = view } ``` -------------------------------- ### Counter Component Example (React) Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Presents a stateful counter component in React using the `useReducer` hook for state management. It shows how to pass state and dispatch functions down to a child `Counter` component and render it using ReactDOM. ```React import React, { useReducer } from 'react'; import ReactDOM from 'react-dom'; import Counter, { applyAction, defaultState } from '../../shared/Counter'; const App = () => { let [state, inject] = useReducer( (state, action) => applyAction(state, action, 1), defaultState ); return ; }; ReactDOM.render(, document.getElementById('app')); ``` -------------------------------- ### Elm Sequential Composition Example Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Illustrates sequential composition in Elm, where the state of the 'how many' counter dictates the number of other counter components rendered. The second counter's update logic depends on the model of the first component. ```elm open! Core open! Import let app graph = let counter_view, how_many = Counter.component ~label:(Bonsai.return "how many") graph in let map = let%map how_many in List.init how_many ~f:(fun i -> i, ()) |> Int.Map.of_alist_exn in let others = Bonsai.assoc (module Int) map graph ~f:(fun key _data graph -> let view, _ = Counter.component ~label:(key >>| Int.to_string) graph in view) in let%map counter_view and others in N.div (counter_view :: Map.data others) ;; let () = Start.start app ``` ```elm module Main exposing (main) import Browser import Counter import Dict exposing (Dict) import Html exposing (Html, div, map) type alias Model = { howMany : Counter.Model, others : Dict Int Counter.Model } init : Model init = { howMany = Counter.init, others = Dict.empty } type Msg = HowMany Counter.Msg | ForKey Int Counter.Msg updateOther which msg = Dict.update which (\m -> Just (Counter.update 1 msg (Maybe.withDefault 0 m)) ) update : Msg -> Model -> Model update appMsg model = case appMsg of HowMany msgHowMany -> { model | howMany = Counter.update 1 msgHowMany model.howMany } ForKey which msg -> { model | others = updateOther which msg model.others } viewOther : Dict Int Counter.Model -> Int -> Html Counter.Msg viewOther models key = case Dict.get key models of Just model -> Counter.view 1 (String.fromInt key) model Nothing -> Counter.view 1 (String.fromInt key) Counter.init view : Model -> Html Msg view model = List.range 0 (model.howMany - 1) |> List.map (i -> Html.map (ForKey i) (viewOther model.others i)) |> List.append [ Html.map HowMany (Counter.view 1 "how many" model.howMany) ] |> div [] main = Browser.sandbox { init = init, update = update, view = view } ``` -------------------------------- ### React Sequential Composition Example Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Demonstrates sequential composition in React using `useReducer`. The state of one counter component influences the update logic of another, showcasing how to handle inter-component dependencies within a single reducer. ```javascript case 'first': return { ...state, first: counterApplyAction(state.first, subAction, 1), }; case 'second': return { ...state, second: counterApplyAction(state.second, subAction, state.first), }; } } const App = () => { let [state, inject] = useReducer(applyAction, defaultState); let injectFirst = (subAction) => inject({ which: 'first', subAction }); let injectSecond = (subAction) => inject({ which: 'second', subAction }); return (
); }; ReactDOM.render(, document.getElementById('app')); ``` -------------------------------- ### React Parallel Composition Example Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md This React (JSX) code demonstrates parallel composition by rendering two independent Counter components. It uses the `useReducer` hook to manage the state for each counter. ```jsx import React, { useReducer } from 'react'; import ReactDOM from 'react-dom'; import Counter, { applyAction, defaultState } from '../../shared/Counter'; const App = () => { let [state1, inject1] = useReducer( (state, action) => applyAction(state, action, 1), defaultState ); let [state2, inject2] = useReducer( (state, action) => applyAction(state, action, 1), defaultState ); return (
); }; ReactDOM.render(, document.getElementById('app')); ``` -------------------------------- ### Bonsai Counter Component Usage Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md Demonstrates how to use a Bonsai counter component, passing a label and utilizing the default 'by' value. ```ocaml open! Core open! Import let app graph = let view, _ = Counter.component ~label:(Bonsai.return "counter") graph in view ;; let () = Start.start app ``` -------------------------------- ### Elm Counter Component Usage Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md Shows the Elm approach to using a counter component, configuring it with an initial value and a label, and integrating it into a browser sandbox. ```elm module Main exposing (main) import Browser import Counter update = Counter.update 1 view = Counter.view 1 "counter" main = Browser.sandbox { init = Counter.init, update = update, view = view } ``` -------------------------------- ### Bonsai Parallel Composition Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Shows parallel composition in Bonsai. It defines an `app` graph that instantiates two `Counter.component`s in parallel. ```Bonsai open! Core open! Import let app graph = let first_view, by = Counter.component ~label:(Bonsai.return "first") graph in let second_view, _ = Counter.component ~label:(Bonsai.return "second") ~by graph in let%map first = first_view and second = second_view in N.div [ first; second ] ;; let () = Start.start app ``` -------------------------------- ### Bonsai Counter Component (OCaml) Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Defines a configurable counter component using the Bonsai framework. It handles state management for incrementing/decrementing a value and accepts a label and a delta value for configuration. The component exposes its view and state. ```OCaml open! Core open! Import let apply_action _ctx by model = function | `Incr -> model + by | `Decr -> model - by ;; let component ~label ?(by = Bonsai.return 1) graph = let state, inject = Bonsai.state_machine1 ~default_model:0 ~apply_action by graph in let view = let%map state and inject and by and label in let button op action = N.button ~attr:(A.on_click (fun _ -> inject action)) [ N.textf "%s%d" op by ] in N.div [ Vdom.Node.span [ N.textf "%s: " label ] ; button "-" Decr ; Vdom.Node.span [ N.textf "%d" state ] ; button "+" Incr ] in view, state ;; ``` -------------------------------- ### Bonsai Counter Component (OCaml) Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md Defines a stateful counter component using Bonsai. It supports a configurable label and increment/decrement delta. The component exposes both its view and its current state. ```ocaml open! Core open! Import let apply_action _ctx by model = function | `Incr -> model + by | `Decr -> model - by ;; let component ~label ?(by = Bonsai.return 1) graph = let state, inject = Bonsai.state_machine1 ~default_model:0 ~apply_action by graph in let view = let%map state and inject and by and label in let button op action = N.button ~attr:(A.on_click (fun _ -> inject action)) [ N.textf "%s%d" op by ] in N.div [ Vdom.Node.span [ N.textf "%s: " label ] ; button "-" Decr ; Vdom.Node.span [ N.textf "%d" state ] ; button "+" Incr ] in view, state ;; ``` -------------------------------- ### Basic CSS Styling Source: https://github.com/tyoverby/composition-comparison/blob/main/03-sequential/bonsai-2023/index.html Applies basic padding and margin resets to the main body element. ```css body { padding: 0; margin: 0; } ``` -------------------------------- ### Bonsai 2023 Dynamic Counter Component Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md This OCaml code uses Bonsai to dynamically render multiple counter components based on the value of a 'how many' counter. It utilizes Bonsai.assoc to manage the creation and rendering of these dynamic subcomponents. ```ocaml open! Core open! Import let app graph = let counter_view, how_many = Counter.component ~label:(Bonsai.return "how many") graph in let map = let%map how_many in List.init how_many ~f:(fun i -> i, ()) |> Int.Map.of_alist_exn in let others = Bonsai.assoc (module Int) map graph ~f:(fun key _data graph -> let view, _ = Counter.component ~label:(key >>| Int.to_string) graph in view) in let%map counter_view and others in N.div (counter_view :: Map.data others) ;; let () = Start.start app ``` -------------------------------- ### Elm Sequential Composition Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md Elm code demonstrating sequential composition by passing the first counter's model to the second counter's update and view functions. ```elm module Main exposing (main) import Browser import Counter import Html exposing (Html, div) type alias Model = { first : Counter.Model, second : Counter.Model } init : Model init = { first = Counter.init, second = Counter.init } type Msg = First Counter.Msg | Second Counter.Msg update : Msg -> Model -> Model update msg model = case msg of First msg_first -> { model | first = Counter.update 1 msg_first model.first } Second msg_second -> { model | second = Counter.update model.first msg_second model.second } view : Model -> Html Msg view model = div [] [ Counter.view 1 "first" model.first |> Html.map First , Counter.view model.first "second" model.second |> Html.map Second ] main = Browser.sandbox { init = init, update = update, view = view } ``` -------------------------------- ### Elm Sequential Composition Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Demonstrates sequential composition in Elm, where the state of one counter influences the other. The `update` function and `view` logic are adjusted accordingly. ```Elm module Main exposing (main) import Browser import Counter import Html exposing (Html, div) type alias Model = { first : Counter.Model, second : Counter.Model } init : Model init = { first = Counter.init, second = Counter.init } type Msg = First Counter.Msg | Second Counter.Msg update : Msg -> Model -> Model update msg model = case msg of First msg_first -> { model | first = Counter.update 1 msg_first model.first } Second msg_second -> { model | second = Counter.update model.first msg_second model.second } view : Model -> Html Msg view model = div [] [ Counter.view 1 "first" model.first |> Html.map First , Counter.view model.first "second" model.second |> Html.map Second ] main = Browser.sandbox { init = init, update = update, view = view } ``` -------------------------------- ### CSS Main Body Styling Source: https://github.com/tyoverby/composition-comparison/blob/main/01-basic/bonsai/index.html Applies zero padding and margin to the main body element for consistent layout. ```CSS Main body { padding: 0; margin: 0; } ``` -------------------------------- ### CSS Main Body Styling Source: https://github.com/tyoverby/composition-comparison/blob/main/02-parallel/bonsai/index.html Applies zero padding and margin to the main body element for consistent layout. ```CSS Main body { padding: 0; margin: 0; } ``` -------------------------------- ### CSS Main Body Styling Source: https://github.com/tyoverby/composition-comparison/blob/main/03-sequential/bonsai/index.html Applies zero padding and margin to the main body element for consistent layout. ```CSS Main body { padding: 0; margin: 0; } ``` -------------------------------- ### Bonsai 2023 Sequential Composition Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md OCaml code using Bonsai to compose two counter components sequentially. The state of the first counter influences the second. ```ocaml open! Core open! Import let app graph = let first_view, by = Counter.component ~label:(Bonsai.return "first") graph in let second_view, _ = Counter.component ~label:(Bonsai.return "second") ~by graph in let%map first = first_view and second = second_view in N.div [ first; second ] ;; let () = Start.start app ``` -------------------------------- ### Elm Parallel Composition Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Demonstrates parallel composition in Elm. It sets up two independent counter components and maps their messages to a parent message type. ```Elm import Html exposing (Html, div) type alias Model = { first : Counter.Model, second : Counter.Model } init : Model init = { first = Counter.init, second = Counter.init } type Msg = First Counter.Msg | Second Counter.Msg update : Msg -> Model -> Model update msg model = case msg of First msg_first -> { model | first = Counter.update 1 msg_first model.first } Second msg_second -> { model | second = Counter.update 1 msg_second model.second } view : Model -> Html Msg view model = div [] [ Html.map First (Counter.view 1 "first" model.first) , Html.map Second (Counter.view 1 "second" model.second) ] main = Browser.sandbox { init = init, update = update, view = view } ``` -------------------------------- ### CSS Main Body Styling Source: https://github.com/tyoverby/composition-comparison/blob/main/04-multiplicity/bonsai/index.html Applies zero padding and margin to the main body element for consistent layout. ```CSS Main body { padding: 0; margin: 0; } ``` -------------------------------- ### CSS Main Body Styling Source: https://github.com/tyoverby/composition-comparison/blob/main/01-basic/bonsai-2023/index.html Applies zero padding and margin to the main body element for consistent layout. ```CSS Main body { padding: 0; margin: 0; } ``` -------------------------------- ### CSS Main Body Styling Source: https://github.com/tyoverby/composition-comparison/blob/main/02-parallel/bonsai-2023/index.html Applies zero padding and margin to the main body element for consistent layout. ```CSS Main body { padding: 0; margin: 0; } ``` -------------------------------- ### CSS Styling for Document Elements Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html This snippet defines general CSS styles for HTML elements such as html, body, headings, lists, and code blocks. It includes responsive design considerations for smaller screens and print styles. ```css html { line-height: 1.5; font-family: "Inter", sans-serif; font-size: 20px; color: #1a1a1a; background-color: #fdfdfd; } body { margin: 0 auto; padding-left: 50px; padding-right: 50px; padding-top: 50px; padding-bottom: 50px; hyphens: auto; overflow-wrap: break-word; text-rendering: optimizeLegibility; font-kerning: normal; } @media (max-width: 600px) { body { font-size: 0.9em; padding: 1em; } h1 { font-size: 1.8em; } } @media print { body { background-color: transparent; color: black; font-size: 12pt; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3, h4 { page-break-after: avoid; } } p { margin: 1em 0; } a { color: #1a1a1a; } a:visited { color: #1a1a1a; } img { max-width: 100%; } h1, h2, h3, h4, h5, h6 { margin-top: 1.4em; } h5, h6 { font-size: 1em; font-style: italic; } h6 { font-weight: normal; } ol, ul { padding-left: 1.7em; margin-top: 1em; } li > ol, li > ul { margin-top: 0; } blockquote { margin: 1em 0 1em 1.7em; padding-left: 1em; border-left: 2px solid #e6e6e6; color: #606060; } code { font-family: Menlo, Monaco, 'Lucida Console', Consolas, monospace; font-size: 85%; margin: 0; } pre { margin: 1em 0; overflow: auto; } pre code { padding: 0; overflow: visible; overflow-wrap: normal; } .sourceCode { background-color: transparent; overflow: visible; } hr { background-color: #1a1a1a; border: none; height: 1px; margin: 1em 0; } table { margin: 1em 0; border-collapse: collapse; width: 100%; overflow-x: auto; display: block; font-variant-numeric: lining-nums tabular-nums; } table caption { margin-bottom: 0.75em; } tbody { margin-top: 0.5em; border-top: 1px solid #1a1a1a; border-bottom: 1px solid #1a1a1a; } th { border-top: 1px solid #1a1a1a; padding: 0.25em 0.5em 0.25em 0.5em; } td { padding: 0.125em 0.5em 0.25em 0.5em; } header { margin-bottom: 4em; text-align: center; } #TOC li { list-style: none; } #TOC ul { padding-left: 1.3em; } #TOC > ul { padding-left: 0; } #TOC a:not(:hover) { text-decoration: none; } code{white-space: pre-wrap;} span.smallcaps{font-variant: small-caps;} span.underline{text-decoration: underline;} div.column{display: inline-block; vertical-align: top; width: 50%;} div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} ul.task-list{list-style: none;} pre > code.sourceCode { white-space: pre; position: relative; } pre > code.sourceCode > span { display: inline-block; line-height: 1.25; min-height:1em; } pre > code.sourceCode > span:empty { height: 1.2em; } .sourceCode { overflow: visible; } code.sourceCode > span { color: inherit; text-decoration: inherit; } div.sourceCode { } @media screen { div.sourceCode { overflow: auto; } } @media print { pre > code.sourceCode { white-space: pre-wrap; } pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; } } pre.numberSource code { counter-reset: source-line 0; } pre.numberSource code > span { position: relative; left: -4em; counter-increment: source-line; } pre.numberSource code > span > a:first-child::before { content: counter(source-line); position: relative; left: -1em; text-align: right; vertical-align: baseline; border: none; display: inline-block; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; padding: 0 4px; width: 4em; color: #aaaaaa; } pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; } div.sourceCode { } @media screen { pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; } } code span.al { color: #ff0000; font-weight: bold; } /* Alert */ code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */ code span.at { color: #7d9029; } /* Attribute */ code span.bn { color: #40a070; } /* BaseN */ code span.bu { } /* BuiltIn */ code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */ code span.ch { color: #4070a0; } /* Char */ code span.cn { color: #880000; } /* Constant */ code span.co { color: #60a0b0; font-style: italic; } /* Comment */ code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */ code span.do { color: #ba2121; font-style: italic; } /* Documentation */ code span.dt { color: #902000; } /* DataType */ code span.dv { color: #40a070; } /* DecVal */ code span.er { color: #ff0000; font-weight: bold; } /* Error */ code span.ex { } /* Extension */ code span.fl { color: #40a070; } /* Float */ code span.fu { color: #06287e; } /* Function */ code span.im { } /* Import */ code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */ code span.kw { color: #007020; font-weight: bold; } /* Keyword */ code span.op { color: #666666; } /* Operator */ code span.ot { color: #007020; } /* Other */ code span.pp { color: #bc7a00; } /* Preprocessor */ code span.sc { color: #4070a0; } /* SpecialChar */ code span.ss { color: #bb6688; } /* SpecialString */ code span.st { col ``` -------------------------------- ### CSS Main Body Styling Source: https://github.com/tyoverby/composition-comparison/blob/main/04-multiplicity/bonsai-2023/index.html Applies zero padding and margin to the main body element for consistent layout. ```CSS Main body { padding: 0; margin: 0; } ``` -------------------------------- ### React App with useReducer for Dynamic Components Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html This snippet demonstrates a React application that uses the `useReducer` hook to manage two independent states: the number of subcomponents to render and the state of each subcomponent. It dynamically creates subcomponents based on the 'howMany' state and passes down a specific reducer function to each. ```jsx import React, { useReducer } from 'react'; import ReactDOM from 'react-dom'; import Counter from '../../shared/Counter'; import { applyAction as counterApplyAction, defaultState as counterDefaultState } from '../../shared/Counter'; const defaultState = {}; function applyAction(state, { which, subAction }) { return { ...state, [which]: counterApplyAction(state[which] || 0, subAction, 1), }; } const App = () => { let [howMany, injectHowMany] = useReducer( (state, action) => counterApplyAction(state, action, 1), counterDefaultState ); let [subcomponentState, subcomponentInject] = useReducer(applyAction, defaultState); let subcomponents = Array.from({ length: howMany }, function (_, i) { let injectMe = (subAction) => subcomponentInject({ which: i, subAction }); return ( ); }); return (
{subcomponents}
); }; ReactDOM.render(, document.getElementById('app')); ``` -------------------------------- ### React Counter Component Usage Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md Illustrates the React implementation for a counter component, using `useReducer` for state management and passing the state and an action dispatcher to the component. ```javascript import React, { useReducer } from 'react'; import ReactDOM from 'react-dom'; import Counter, { applyAction, defaultState } from '../../shared/Counter'; const App = () => { let [state, inject] = useReducer( (state, action) => applyAction(state, action, 1), defaultState ); return ; }; ReactDOM.render(, document.getElementById('app')); ``` -------------------------------- ### React Sequential Composition Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Illustrates sequential composition in React. The `applyAction` function is modified to handle the dependency where one counter's state affects another. ```React import React, { useReducer } from 'react'; import ReactDOM from 'react-dom'; import Counter, { applyAction as counterApplyAction, defaultState as counterDefaultState, } from '../../shared/Counter'; const defaultState = { first: counterDefaultState, second: counterDefaultState, }; function applyAction(state, { which, subAction }) { switch (which) { ``` -------------------------------- ### Elm Dynamic Counter Component Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md This Elm code implements dynamic rendering of counter components. It manages the state of a 'how many' counter and a dictionary of other counter components, updating and displaying them based on the 'how many' value. ```elm module Main exposing (main) import Browser import Counter import Dict exposing (Dict) import Html exposing (Html, div, map) type alias Model = { howMany : Counter.Model, others : Dict Int Counter.Model } init : Model init = { howMany = Counter.init, others = Dict.empty } type Msg = HowMany Counter.Msg | ForKey Int Counter.Msg updateOther which msg = Dict.update which (\_ -> Just (Counter.update 1 msg (Maybe.withDefault 0 m)) ) update : Msg -> Model -> Model update appMsg model = case appMsg of HowMany msgHowMany -> { model | howMany = Counter.update 1 msgHowMany model.howMany } ForKey which msg -> { model | others = updateOther which msg model.others } viewOther : Dict Int Counter.Model -> Int -> Html Counter.Msg viewOther models key = case Dict.get key models of Just model -> Counter.view 1 (String.fromInt key) model Nothing -> Counter.view 1 (String.fromInt key) Counter.init view : Model -> Html Msg view model = List.range 0 (model.howMany - 1) |> List.map (i -> Html.map (ForKey i) (viewOther model.others i)) |> List.append [ Html.map HowMany (Counter.view 1 "how many" model.howMany) ] |> div [] main = Browser.sandbox { init = init, update = update, view = view } ``` -------------------------------- ### Elm Counter Component Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md An Elm module defining a counter component. It exports the initial model, update function, and view calculations. The update and view functions require the increment/decrement amount and a label. ```elm module Counter exposing (Model, Msg, init, update, view) import Browser import Html exposing (Html, div, span, text) import Html.Events exposing (onClick) type alias Model = Int init : Model init = 0 type Msg = Increment | Decrement update : Int -> Msg -> Model -> Model update howMuch msg model = case msg of Increment -> model + howMuch Decrement -> model - howMuch view : Int -> String -> Model -> Html Msg view howMuch label model = let button op action = Html.button [ onClick action ] [ text (String.concat [ op, String.fromInt howMuch ]) ] in div [] [ text (String.concat [ label, ": " ]) , button "-" Decrement , text (String.fromInt model) , button "+" Increment ] ;; ``` -------------------------------- ### React Counter Component Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html A React implementation of a counter component. It includes default state, an `applyAction` function to manage state changes based on a delta, and a functional component that renders the label, buttons for increment/decrement, and the current state. ```JavaScript import React from 'react'; export const defaultState = 0; export function applyAction(state, action, by) { switch (action) { case 'increment': return state + by; case 'decrement': return state - by; default: console.error('BUG'); } } const Counter = ({ label, by, state, inject }) => { let increment = () => inject('increment'); let decrement = () => inject('decrement'); return (
{label}: {state}
); }; export default Counter; ``` -------------------------------- ### React Parallel Composition Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Illustrates parallel composition in React using `useReducer`. Two counter components are rendered independently with their own state and dispatch functions. ```React import React, { useReducer } from 'react'; import ReactDOM from 'react-dom'; import Counter, { applyAction, defaultState } from '../../shared/Counter'; const App = () => { let [state1, inject1] = useReducer( (state, action) => applyAction(state, action, 1), defaultState ); let [state2, inject2] = useReducer( (state, action) => applyAction(state, action, 1), defaultState ); return (
); }; ReactDOM.render(, document.getElementById('app')); ``` -------------------------------- ### Elm Counter Component Source: https://github.com/tyoverby/composition-comparison/blob/main/index.html Implements a counter component in Elm. It defines the model, messages for incrementing and decrementing, an update function to handle state changes based on the delta, and a view function to render the counter with a label and buttons. ```Elm module Counter exposing (Model, Msg, init, update, view) import Browser import Html exposing (Html, div, span, text) import Html.Events exposing (onClick) type alias Model = Int init : Model init = 0 type Msg = Increment | Decrement update : Int -> Msg -> Model -> Model update howMuch msg model = case msg of Increment -> model + howMuch Decrement -> model - howMuch view : Int -> String -> Model -> Html Msg view howMuch label model = let button op action = Html.button [ onClick action ] [ text (String.concat [ op, String.fromInt howMuch ]) ] in div [] [ text (String.concat [ label, ": " ]) , button "-" Decrement , text (String.fromInt model) , button "+" Increment ] ``` -------------------------------- ### React Dynamic Counter Component Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md This React code dynamically renders multiple counter components. It uses two useReducer hooks: one for the main 'how many' counter and another for managing the state of the dynamically generated sub-counter components. ```jsx import React, { useReducer } from 'react'; import ReactDOM from 'react-dom'; import Counter, { applyAction as counterApplyAction, defaultState as counterDefaultState, } from '../../shared/Counter'; const defaultState = {}; function applyAction(state, { which, subAction }) { return { ...state, [which]: counterApplyAction(state[which] || 0, subAction, 1), }; } const App = () => { let [howMany, injectHowMany] = useReducer( (state, action) => counterApplyAction(state, action, 1), counterDefaultState ); let [subcomponentState, subcomponentInject] = useReducer(applyAction, defaultState); let subcomponents = Array.from({ length: howMany }, function (_, i) { let injectMe = (subAction) => subcomponentInject({ which: i, subAction }); return ( ); }); return (
{subcomponents}
); }; ReactDOM.render(, document.getElementById('app')); ``` -------------------------------- ### React Sequential Composition Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md React code for sequential composition, using a single reducer to manage state updates from two counter components, allowing them to witness each other's changes. ```jsx import React, { useReducer } from 'react'; import ReactDOM from 'react-dom'; import Counter, { applyAction as counterApplyAction, defaultState as counterDefaultState, } from '../../shared/Counter'; const defaultState = { first: counterDefaultState, second: counterDefaultState, }; function applyAction(state, { which, subAction }) { switch (which) { case 'first': return { ...state, first: counterApplyAction(state.first, subAction, 1), }; case 'second': return { ...state, second: counterApplyAction(state.second, subAction, state.first), }; } } const App = () => { let [state, inject] = useReducer(applyAction, defaultState); let injectFirst = (subAction) => inject({ which: 'first', subAction }); let injectSecond = (subAction) => inject({ which: 'second', subAction }); return (
); }; ReactDOM.render(, document.getElementById('app')); ``` -------------------------------- ### React Counter Component (JSX) Source: https://github.com/tyoverby/composition-comparison/blob/main/readme.md A React counter component implemented in JSX. It exports default state and a state transition function. The component accepts label, delta, state, and an inject function as props. ```jsx import React from 'react'; export const defaultState = 0; export function applyAction(state, action, by) { switch (action) { case 'increment': return state + by; case 'decrement': return state - by; default: console.error('BUG'); } } const Counter = ({ label, by, state, inject }) => { let increment = () => inject('increment'); let decrement = () => inject('decrement'); return (
{label}: {state}
); }; export default Counter; ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.