Timeouts in Haskell

Timeouts are important for programs that connect to external resources or that otherwise need to bound execution time. Implementing timeouts in Haskell can be achieved using the timeout function from the System.Timeout module.

import Control.Concurrent (threadDelay)
import System.Timeout (timeout)
import Control.Monad (void)

main :: IO ()
main = do
    -- For our example, suppose we're executing an external
    -- call that returns its result after 2s. 
    let slowOperation = do
            threadDelay (2 * 1000000)  -- 2 seconds
            return "result 1"

    -- Here we implement a timeout using the `timeout` function.
    -- It will wait for 1 second for the result.
    result1 <- timeout (1 * 1000000) slowOperation
    case result1 of
        Just res -> putStrLn res
        Nothing  -> putStrLn "timeout 1"

    -- If we allow a longer timeout of 3s, then the operation
    -- will succeed and we'll print the result.
    let anotherSlowOperation = do
            threadDelay (2 * 1000000)  -- 2 seconds
            return "result 2"

    result2 <- timeout (3 * 1000000) anotherSlowOperation
    case result2 of
        Just res -> putStrLn res
        Nothing  -> putStrLn "timeout 2"

In this Haskell version:

  1. We use threadDelay to simulate a slow operation. It takes microseconds as an argument, so we multiply seconds by 1,000,000.

  2. The timeout function from System.Timeout is used to implement the timeout mechanism. It takes a time in microseconds and an IO action, and returns Nothing if the action doesn’t complete within the specified time, or Just result if it does.

  3. We use pattern matching with case expressions to handle the results of timeout.

  4. Unlike in the original example, we don’t need to use channels or select statements, as Haskell’s timeout function provides a more straightforward way to implement timeouts.

Running this program shows the first operation timing out and the second succeeding:

$ runhaskell timeouts.hs
timeout 1
result 2

This example demonstrates how to implement timeouts in Haskell, which is useful for bounding the execution time of operations that might take too long to complete.