Timeouts in Elm

In Elm, we don’t have the concept of timeouts in the same way as Go. However, we can simulate similar behavior using Elm’s Time.every and Process.sleep functions. Here’s how we might approach this:

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

type Msg
    = Tick Time.Posix
    | Result1 String
    | Result2 String
    | Timeout1
    | Timeout2

type alias Model =
    { result1 : Maybe String
    , result2 : Maybe String
    }

init : () -> (Model, Cmd Msg)
init _ =
    ( { result1 = Nothing, result2 = Nothing }
    , Cmd.batch
        [ Task.perform Result1 (Process.sleep 2000 |> Task.andThen (\_ -> Task.succeed "result 1"))
        , Task.perform Result2 (Process.sleep 2000 |> Task.andThen (\_ -> Task.succeed "result 2"))
        , Task.perform (\_ -> Timeout1) (Process.sleep 1000)
        , Task.perform (\_ -> Timeout2) (Process.sleep 3000)
        ]
    )

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        Result1 result ->
            ( { model | result1 = Just result }, Cmd.none )
        Result2 result ->
            ( { model | result2 = Just result }, Cmd.none )
        Timeout1 ->
            if model.result1 == Nothing then
                ( { model | result1 = Just "timeout 1" }, Cmd.none )
            else
                ( model, Cmd.none )
        Timeout2 ->
            if model.result2 == Nothing then
                ( { model | result2 = Just "timeout 2" }, Cmd.none )
            else
                ( model, Cmd.none )
        Tick _ ->
            ( model, Cmd.none )

view : Model -> Html Msg
view model =
    div []
        [ div [] [ text <| "Result 1: " ++ Maybe.withDefault "Waiting..." model.result1 ]
        , div [] [ text <| "Result 2: " ++ Maybe.withDefault "Waiting..." model.result2 ]
        ]

main : Program () Model Msg
main =
    Browser.element
        { init = init
        , update = update
        , view = view
        , subscriptions = \_ -> Time.every 1000 Tick
        }

In this Elm program:

  1. We define a Model that holds the results of our two operations.

  2. In the init function, we start four tasks:

    • Two tasks that simulate our “long-running” operations, each taking 2 seconds.
    • Two tasks that act as our timeouts, one for 1 second and one for 3 seconds.
  3. In the update function:

    • If a result arrives before its timeout, we store it in the model.
    • If a timeout occurs before its result, we store “timeout” as the result.
  4. The view function displays the current state of both results.

This Elm program simulates the behavior of the original Go program:

  • The first operation will timeout after 1 second.
  • The second operation will complete successfully after 2 seconds, before its 3-second timeout.

Running this program would show:

Result 1: timeout 1
Result 2: result 2

Note that Elm’s architecture and concurrency model are quite different from Go’s. In Elm, we’re using the Elm Architecture with its update function to handle state changes, rather than using channels and goroutines as in Go.