Tickers in Standard ML

Our first example demonstrates the use of tickers, which are used for performing actions repeatedly at regular intervals. Here’s an example of a ticker that ticks periodically until we stop it.

(* We'll use the Time structure for timing operations *)
structure Time = Time

(* We'll simulate channels using references *)
type 'a channel = 'a option ref

(* Function to create a new channel *)
fun newChannel () = ref NONE

(* Function to send a value on a channel *)
fun send (ch: 'a channel) (value: 'a) =
    ch := SOME value

(* Function to receive a value from a channel *)
fun receive (ch: 'a channel) =
    case !ch of
        SOME value => (ch := NONE; SOME value)
      | NONE => NONE

(* Main function *)
fun main () =
    let
        (* Create a ticker that ticks every 500 milliseconds *)
        val ticker = ref true
        val tickerChannel = newChannel ()
        
        (* Create a done channel *)
        val done = newChannel ()

        (* Function to simulate the ticker *)
        fun tickerFunc () =
            if !ticker then
            (
                send tickerChannel (Time.now());
                Time.sleep (Time.fromMilliseconds 500);
                tickerFunc ()
            )
            else
                ()

        (* Start the ticker in a separate thread *)
        val _ = Thread.spawn tickerFunc

        (* Function to handle ticks *)
        fun handleTicks () =
            case receive tickerChannel of
                SOME t => (
                    print ("Tick at " ^ Time.toString t ^ "\n");
                    handleTicks ()
                )
              | NONE => 
                    if !(receive done) then
                        ()
                    else
                        handleTicks ()

        (* Start handling ticks in a separate thread *)
        val _ = Thread.spawn handleTicks

        (* Sleep for 1600 milliseconds *)
        val _ = Time.sleep (Time.fromMilliseconds 1600)

        (* Stop the ticker *)
        val _ = ticker := false
        val _ = send done true
        val _ = print "Ticker stopped\n"
    in
        ()
    end

(* Run the main function *)
val _ = main ()

In this Standard ML version, we simulate the behavior of channels and tickers using references and threads. The Time structure is used for timing operations.

We create a ticker that sends a value every 500 milliseconds. We use a separate thread to simulate the ticker’s behavior. Another thread is used to handle the ticks, printing the time of each tick.

The main thread sleeps for 1600 milliseconds, allowing about 3 ticks to occur, and then stops the ticker.

When we run this program, the ticker should tick 3 times before we stop it. The output might look something like this:

Tick at Tue Jul 11 10:30:00 2023
Tick at Tue Jul 11 10:30:00.500 2023
Tick at Tue Jul 11 10:30:01 2023
Ticker stopped

Note that Standard ML doesn’t have built-in support for concurrent operations like channels and tickers. This implementation is a simplified simulation of the concept using threads and shared mutable state, which isn’t idiomatic Standard ML. In a real Standard ML program, you would typically use a different approach to handle periodic events.