Signals in Haskell

Here’s the translation of the Go code to Haskell, with explanations in Markdown format suitable for Hugo:

Our first example demonstrates how to handle Unix signals in Haskell. We’ll create a program that gracefully handles SIGINT and SIGTERM signals.

import System.Posix.Signals
import Control.Concurrent (forkIO, threadDelay)
import Control.Concurrent.MVar

main :: IO ()
main = do
    -- Create an MVar to act as a signal between threads
    done <- newEmptyMVar

    -- Set up signal handlers
    installHandler sigINT (Catch $ handleSignal "SIGINT" done) Nothing
    installHandler sigTERM (Catch $ handleSignal "SIGTERM" done) Nothing

    -- Fork a thread to simulate some work
    forkIO $ do
        putStrLn "Awaiting signal"
        threadDelay 100000000  -- Sleep for 100 seconds

    -- Wait for a signal
    takeMVar done
    putStrLn "Exiting"

handleSignal :: String -> MVar () -> IO ()
handleSignal sigName done = do
    putStrLn $ "\nReceived " ++ sigName
    putMVar done ()

In this Haskell version, we use the System.Posix.Signals module to handle Unix signals. Here’s a breakdown of the code:

  1. We import necessary modules for signal handling, concurrency, and inter-thread communication.

  2. In the main function, we create an MVar called done. This will act as a channel for signaling between threads.

  3. We set up signal handlers for SIGINT and SIGTERM using installHandler. Each handler calls the handleSignal function with the appropriate signal name and the done MVar.

  4. We fork a new thread that simulates some work by sleeping for 100 seconds. This is analogous to the main work of a server or long-running process.

  5. The main thread then waits on the done MVar using takeMVar. This blocks until a signal is received and handled.

  6. The handleSignal function prints the name of the received signal and puts a value into the done MVar, which unblocks the main thread.

When we run this program, it will block waiting for a signal. By typing Ctrl-C, we can send a SIGINT signal, causing the program to print the signal name and then exit.

$ runhaskell signals.hs
Awaiting signal
^C
Received SIGINT
Exiting

This Haskell implementation uses MVars for inter-thread communication, which is conceptually similar to channels in other languages. The forkIO function is used to create a new thread, analogous to goroutines in Go.

While the structure is different from the original Go code, this Haskell version achieves the same functionality of gracefully handling Unix signals.