Timers in Standard ML

(* We often want to execute code at some point in the
   future, or repeatedly at some interval. Standard ML's
   Timer structure provides functionality for these tasks. *)

structure Timer = Timer

fun main () =
    let
        (* Timers represent a single event in the future. You
           tell the timer how long you want to wait, and it
           provides a way to be notified at that time.
           This timer will wait 2 seconds. *)
        val timer1 = Timer.startRealTimer()

        (* We wait until the timer reaches 2 seconds *)
        val _ = Timer.checkRealTimer timer1
        val _ = while Timer.checkRealTimer timer1 < Time.fromSeconds 2 do ()
        val _ = print "Timer 1 fired\n"

        (* If you just wanted to wait, you could have used
           OS.Process.sleep. One reason a timer may be useful is
           that you can check it before it reaches the desired time.
           Here's an example of that. *)
        val timer2 = Timer.startRealTimer()
        
        (* We start a new thread that waits for the timer *)
        val _ = Thread.spawn (fn () =>
            let
                val _ = while Timer.checkRealTimer timer2 < Time.fromSeconds 1 do ()
                val _ = print "Timer 2 fired\n"
            in
                ()
            end)

        (* We check if the timer has reached 1 second *)
        val stop2 = Timer.checkRealTimer timer2 < Time.fromSeconds 1
        val _ = if stop2 then print "Timer 2 stopped\n" else ()

        (* Give enough time for timer2 to fire, if it ever
           was going to, to show it is in fact stopped. *)
        val _ = OS.Process.sleep (Time.fromSeconds 2)
    in
        ()
    end

val _ = main()

To run this program, you would typically save it to a file (e.g., timers.sml) and then use an SML interpreter or compiler to execute it. For example, if you’re using Standard ML of New Jersey (SML/NJ), you could run it like this:

$ sml timers.sml
Timer 1 fired
Timer 2 stopped

Note that Standard ML doesn’t have built-in timers that work exactly like Go’s, so this example uses the Timer structure to approximate similar functionality. The Thread.spawn function is used to simulate concurrent behavior, although it’s not a perfect equivalent to Go’s goroutines.

Also, Standard ML doesn’t have a direct equivalent to Go’s channels, so the waiting mechanism is implemented differently. Instead of blocking on a channel, we use a while loop to check the timer’s status repeatedly.

This example demonstrates basic timer usage in Standard ML, including creating timers, waiting for them to fire, and checking their status before they complete. The structure of the program and its explanation have been adapted to fit Standard ML’s syntax and idioms while maintaining the original concept of working with timers.