Select in Standard ML
The select
statement in Standard ML can be simulated using the CML
(Concurrent ML) library, which provides concurrent programming features. However, since Standard ML doesn’t have built-in concurrency primitives like channels, we’ll use threads and synchronization primitives to demonstrate a similar concept.
structure Select =
struct
fun main () =
let
val mutex = Mutex.mutex ()
val cond = ConditionVar.conditionVar ()
val c1 = ref NONE
val c2 = ref NONE
fun sender1 () =
let
val _ = OS.Process.sleep (Time.fromSeconds 1)
val _ = Mutex.lock mutex
val _ = c1 := SOME "one"
val _ = ConditionVar.signal cond
val _ = Mutex.unlock mutex
in
()
end
fun sender2 () =
let
val _ = OS.Process.sleep (Time.fromSeconds 2)
val _ = Mutex.lock mutex
val _ = c2 := SOME "two"
val _ = ConditionVar.signal cond
val _ = Mutex.unlock mutex
in
()
end
fun receiver () =
let
fun loop 0 = ()
| loop n =
let
val _ = Mutex.lock mutex
val _ = ConditionVar.wait (cond, mutex)
val _ =
case (!c1, !c2) of
(SOME msg1, _) =>
(print ("received " ^ msg1 ^ "\n");
c1 := NONE)
| (_, SOME msg2) =>
(print ("received " ^ msg2 ^ "\n");
c2 := NONE)
| _ => ()
val _ = Mutex.unlock mutex
in
loop (n-1)
end
in
loop 2
end
in
Thread.spawn sender1;
Thread.spawn sender2;
receiver ()
end
end
val _ = Select.main ()
This Standard ML code simulates the behavior of the original example. Here’s how it works:
We define two reference cells
c1
andc2
to simulate channels.Two sender functions (
sender1
andsender2
) are defined to simulate the goroutines in the original code. They sleep for 1 and 2 seconds respectively, then set their corresponding reference cells.The
receiver
function simulates theselect
statement. It uses a mutex and condition variable to wait for signals from the senders.In the
main
function, we spawn two threads for the senders and call the receiver function.The receiver loops twice, each time waiting for a signal and then checking which “channel” has a value.
To run this program, you would need to compile it with a Standard ML compiler that supports threads, such as MLton. The output would be similar to the original:
received one
received two
Note that the total execution time would still be about 2 seconds, as both “channels” are processed concurrently.
This example demonstrates how to simulate channel-like behavior and selection in Standard ML using threads and synchronization primitives. While it’s not as elegant as Go’s select
statement, it achieves a similar result in terms of concurrent execution and communication between threads.