Reading Note: Interacting with time in elm

2022-04-02softwareelmfrontend

There are two ways to interact with time in Elm.

To understand the command and subscription in Elm I recommend you to read the Elm documentation and this article at first. Moreover, you also have to learn the usages of Process and Task in Elm.

Via a command

A command is a way of telling Elm, “Hey, I want you to do this thing!”. So, if you want to send an HTTP request, you would need to command Elm to do it. Or if you wanted to ask for geolocation, you would need to command Elm to go get it.

We can use the command in Elm to count down the time and notify us after the timer is expired. For example:

type Model
  = Initial
  | InProgress
  | Expired

init : () -> (Model, Cmd Msg)
init _ =
  (Initial, Cmd.none)

type Msg
  = StartTimer
  | TimeIsUp

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    StartTimer ->
      (InProgress, notifyIn TimeIsUp 5000)
    TimeIsUp ->
      case model of
        InProgress ->
          (Expired, Cmd.none)
        _ ->
          (model, Cmd.none)

notifyIn : Msg -> Float -> Cmd Msg
notifyIn msg time =
  Process.sleep time |> Task.attempt (\_ -> msg)

In the code above, the important part is:

StartTimer ->
  (InProgress, notifyIn TimeIsUp 5000)

This code switches the model from the Initial state to the InProgress state. The command kick off a timer which will notify us of the TimeIsUp message after 5 seconds have passed.

notifyIn : Msg -> Float -> Cmd Msg
notifyIn msg time =
  Process.sleep time |> Task.attempt (\_ -> msg)

Via Subscription

A subscription is a way of telling Elm, “Hey, let me know if anything interesting happens over there!”. So, if you want to listen for messages on a web socket, you would tell Elm to create a subscription.

In this case, you would like Elm to get the clock ticks.

type Model
  = Initial
  | InProgress
  | Expired

init : () -> (Model, Cmd Msg)
init _ =
  (Initial, Cmd.none)

type Msg
  = StartTimer
  | Tick

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    StartTimer ->
      (InProgress, Cmd.none)
    Tick ->
      case model of
        InProgress 1 ->
          (Expired, Cmd.none)
        InProgress x ->
          (InProgress (x - 1), Cmd.none)
        _ ->
          (model, Cmd.none)

notifyIn : Msg -> Float -> Cmd Msg
notifyIn msg time =
  Process.sleep time |> Task.attempt (\_ -> msg)

subscriptions : Model -> Sub Msg
subscriptions model =
  case model of
    Initial -> Sub.none
    InProgress _ -> Time.every 1000 (\_ -> Tick)
    Expired -> Sub.none

The subscription function triggers a Tick message every second when the game is in the InProgress state.

subscription : model -> Sub Msg
  case model of
    Initial -> Sub.none
    InProgress _ -> Time.every 1000 (\_ -> Tick)
    Expired -> Sub.none

References