{-# OPTIONS -fglasgow-exts #-} -- Even simpler IO Regions, in Haskell98 + existentials -- $Id: IORegions98.hs,v 1.1 2006/01/24 00:19:48 oleg Exp oleg $ module IORegions98E (runIOM, qGetChar, withFile, -- Only types are exported, not their data constructors Q, IOM ) where import Control.Exception import System.IO import Data.Typeable -- The marked IO monad. The data constructor is not exported. -- Monad derivation is not supported in Haskell98, we have to do it by hand -- newtype IOM marks a = IOM (IO a) deriving Monad newtype IOM marks a = IOM (IO a) unIOM (IOM x) = x instance Monad (IOM marks) where return = IOM . return m >>= f = IOM (unIOM m >>= unIOM . f) -- The marked IO handle. The data constructor is not exported. newtype Q mark = Q Handle -- The only mark we use here is polymorphic. Therefore, it is not -- the instance of Typeable. -- Reading from a marked handle. The mark must be within the marks -- associated with the IOM monad. We only use one mark: -- since we prevent escaping of the Q handle from the inner scope, -- the only handles in scope must be the ones created in the current -- or outer regions. qGetChar :: Q mark -> IOM mark Char qGetChar (Q h) = IOM $ hGetChar h -- There must not be an operation to close a marked handle! -- withFile takes care of opening and closing (and disposing) of -- handles. -- If one really must close the handle prematurely, throw any exception. -- When the exception reaches withFile, the handle will be closed. -- If the handle is to be closed, the rest of its region becomes useless -- anyway. -- Open the file, and make sure the marked handle does not escape. -- The marked handle is closed on normal or abnormal exit from the -- body -- The type system guarantees the strong lexical scoping of -- withFile. That is, we can assuredly close all the handles after -- we leave withFile because we are assured that no computations with -- marked handles can occur after we leave withFile. -- Because |IOM mark a| and |Q mark| must be fully polymorphic with -- respect to |mark| to be usable for |runIOM|, these types cannot -- be instances of Typeable, because Typeable does not apply to -- polymorphic types. So, neither |IOM mark b| nor |Q mark| may -- appear within |a|. withFile :: Typeable a => FilePath -> (Q mark -> IOM mark a) -> IOM mark a withFile filename proc = IOM( bracket (openFile filename ReadMode) (hClose) (\handle -> unIOM $ proc (Q handle))) -- Running the IOM monad runIOM :: (forall mark. IOM mark a) -> IO a runIOM x = unIOM x