module FictionalDevice ( device1 , device2 , DeviceHandle , initDevice , someDeviceAction ) import Control.Concurrent import Control.Concurrent.MVar type BaseAddress = ... data DeviceState = DeviceState { isFirstAccess :: Bool , baseAddress :: BaseAddress , ... } initialDeviceState :: BaseAddress -> DeviceState initialDeviceState addr = DeviceState { isFirstAccess = True , baseAddress = addr , ... } device1baseAddresss :: BaseAddress device1baseAddress = ... device2baseAddresss :: BaseAddress device2baseAddress = ... threadlocal device1state (MVar DeviceState) (initTL (newMVarTL (initialDeviceState device1baseAddress))) threadlocal device2state (MVar DeviceState) (initTL (newMVarTL (initialDeviceState device2baseAddress))) newtype DeviceHandle = DH (TLRef (MVar DeviceState)) device1 = DH device1state device2 = DH device2state doInitDevice :: BaseAddress -> IO () doInitDevice addr = ... initDevice :: DeviceHandle -> IO () initDevice (DH ref) = do stMvar <- readThreadLocal ref modifyMVar_ stMVar (\st -> do when (isFirstAccess st) (doInitDevice (baseAddress st)) return st{ isFirstAccess = False }) someDeviceAction :: DeviceHandle -> IO () someDeviceAdction h@(DH ref) = do initDevice h ...