We can use the unit
type as a function argument to define functions that we don't want executed until later. This is often useful in asynchronous background tasks, when the main thread may want trigger some predefined functionality of the background thread, like maybe moving it to a new file, or if a a let-binding should not be run immediately:
module Time =
let now = System.DateTime.Now // value is set and fixed for duration of program
let now() = System.DateTime.Now // value is calculated when function is called (each time)
In the following code, we define code to start a "worker" which simply prints out the value it is working on every 2 seconds. The worker then returns two functions which may be used to control it - one which moves it to the next value to work on, and one which stops it from working. These must be functions, because we do not want their bodies to be executed until we choose to, otherwise the worker would immediately move to the second value and shutdown without having done anything.
let startWorker value =
let current = ref value
let stop = ref false
let nextValue () = current := !current + 1
let stopOnNextTick () = stop := true
let rec loop () = async {
if !stop then
printfn "Stopping work."
return ()
else
printfn "Working on %d." !current
do! Async.Sleep 2000
return! loop () }
Async.Start (loop ())
nextValue, stopOnNextTick
We can then start a worker by doing
let nextValue, stopOnNextTick = startWorker 12
and the work will begin - if we are in F# interactive, we will see the messages printed out in the console every two seconds. We can then run
nextValue ()
and we will see the messages indicating that value being worked on has moved to the next one.
When it is time to finish working, we can run the
stopOnNextTick ()
function, which will print out the closing message, then exit.
The unit
type is important here to signify "no input" - the functions already have all the information they need to work built into them, and the caller is not allowed to change that.