Atomic Counters in Elm

In Elm, we don’t have the concept of shared mutable state or goroutines. Instead, we’ll simulate the atomic counter behavior using the Elm architecture with a single state and update function. We’ll use the Random module to simulate concurrent updates.

import Browser
import Html exposing (Html, div, text)
import Random
import Task

-- MODEL

type alias Model =
    { counter : Int
    }

init : () -> (Model, Cmd Msg)
init _ =
    ( { counter = 0 }
    , generateUpdates
    )

-- UPDATE

type Msg
    = Increment
    | Complete

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        Increment ->
            ( { model | counter = model.counter + 1 }
            , Cmd.none
            )
        
        Complete ->
            ( model
            , Cmd.none
            )

-- SUBSCRIPTIONS

subscriptions : Model -> Sub Msg
subscriptions _ =
    Sub.none

-- VIEW

view : Model -> Html Msg
view model =
    div []
        [ text ("Counter: " ++ String.fromInt model.counter)
        ]

-- COMMANDS

generateUpdates : Cmd Msg
generateUpdates =
    List.range 1 50
        |> List.map (\_ -> generateThousandUpdates)
        |> Cmd.batch

generateThousandUpdates : Cmd Msg
generateThousandUpdates =
    List.range 1 1000
        |> List.map (\_ -> Random.generate (\_ -> Increment) Random.bool)
        |> Cmd.batch
        |> Task.perform identity

-- MAIN

main : Program () Model Msg
main =
    Browser.element
        { init = init
        , update = update
        , subscriptions = subscriptions
        , view = view
        }

In this Elm program:

  1. We define a Model with a single counter field.

  2. The init function sets up the initial state and triggers the generation of updates.

  3. We have two types of messages: Increment to increase the counter and Complete (unused in this example but could be used for additional functionality).

  4. The update function handles the Increment message by increasing the counter.

  5. The view function displays the current counter value.

  6. generateUpdates creates 50 batches of 1000 random update commands, simulating the behavior of 50 goroutines each incrementing 1000 times.

  7. generateThousandUpdates creates 1000 random update commands.

  8. The main function sets up the Elm application.

To run this program:

  1. Save it as AtomicCounter.elm
  2. Use the Elm compiler to compile it: elm make AtomicCounter.elm
  3. Open the resulting index.html in a web browser

You should see the counter value displayed, which will be updated to 50,000 almost instantly due to the simulation of concurrent updates.

Note that this is not truly concurrent or atomic in the same way as the original example, but it demonstrates how you might approach a similar problem in Elm’s functional, single-state-tree model.