The Hugs-GHC Extension Libraries <author>Alastair Reid <tt/reid-alastair@cs.yale.edu/ Simon Marlow <tt/simonm@dcs.gla.ac.uk/ <date>v0.8, 14 December 1997 <abstract> Hugs and GHC provide a common set of libraries to aid portability. This document specifies the interfaces to these libraries and documents known differences. We hope that these modules will be adopted for inclusion as Standard Haskell Libraries sometime soon. </abstract> <toc> <sect> <idx/ST/ <label id="sec:ST"> <p> This library provides support for <em/strict/ state threads, as described in the PLDI '94 paper by John Launchbury and Simon Peyton Jones <cite id="LazyStateThreads">. In addition to the monad <tt/ST/, it also provides mutable variables <tt/STRef/ and mutable arrays <tt/STArray/. <tscreen><verb> module ST( module ST, module Monad ) where import Monad data ST s a -- abstract type runST :: forall a. (forall s. ST s a) -> a fixST :: (a -> ST s a) -> ST s a unsafeInterleaveST :: ST s a -> ST s a instance Functor (ST s) instance Monad (ST s) data STRef s a -- mutable variables in state thread s -- containing values of type a. newSTRef :: a -> ST s (STRef s a) readSTRef :: STRef s a -> ST s a writeSTRef :: STRef s a -> a -> ST s () instance Eq (STRef s a) data STArray s ix elt -- mutable arrays in state thread s -- indexed by values of type ix -- containing values of type a. newSTArray :: Ix ix => (ix,ix) -> elt -> ST s (STArray s ix elt) boundsSTArray :: Ix ix => STArray s ix elt -> (ix, ix) readSTArray :: Ix ix => STArray s ix elt -> ix -> ST s elt writeSTArray :: Ix ix => STArray s ix elt -> ix -> elt -> ST s () thawSTArray :: Ix ix => Array ix elt -> ST s (STArray s ix elt) freezeSTArray :: Ix ix => STArray s ix elt -> ST s (Array ix elt) unsafeFreezeSTArray :: Ix ix => STArray s ix elt -> ST s (Array ix elt) instance Eq (STArray s ix elt) </verb></tscreen> Notes: <itemize> <item> GHC also supports ByteArrays --- these aren't supported by Hugs yet. <item> The operations <tt/freezeSTArray/ and <tt/thawSTArray/ convert mutable arrays to and from immutable arrays. Semantically, they are identical to copying the array and they are usually implemented that way. The operation <tt/unsafeFreezeSTArray/ is a faster version of <tt/freezeSTArray/ which omits the copying step. It's a safe substitute for <tt/freezeSTArray/ if you don't modify the mutable array after freezing it. <item> In the current version of Hugs, the <tt/<idx/runST// operation, used to specify encapsulation, is implemented as a language construct, and <tt/runST/ is treated as a keyword. We plan to change this to match GHC soon. <!-- <item> Note that it is possible to install Hugs 1.4 without support for lazy state threads, and hence the primitives described here may not be available in all implementations. Also, in contrast with the implementation of lazy state threads in previous releases of Hugs and Gofer, there is no direct relationship between the <tt/<idx/ST monad// and the <tt/<idx/IO monad//. --> <item> Hugs provides <tt/thenLazyST/ and <tt/thenStrictST/ so that you can import <tt/LazyST/ (say) and still use the strict instance in those places where it matters. GHC implements LazyST and ST using different types, so this isn't possible. </item> </itemize> <sect> <idx/LazyST/ <label id="sec:LazyST"> <p> This library is identical to <tt/ST/ except that the <tt/ST/ monad instance is <em/lazy/. The lazy ST monad tends to be more prone to space leaks than the strict version, so most programmers will use the former unless laziness is explicitly required. <tt/LazyST/ provides two additional operations: <tscreen> <verb> lazyToStrictST :: LazyST.ST s a -> ST.ST s a strictToLazyST :: ST.ST s a -> LazyST.ST s a </verb> </tscreen> These are used to convert between lazy and strict state threads. The semantics with respect to laziness are as you would expect: the strict state thread passed to <tt/strictToLazyST/ is not performed until the result of the lazy state thread it returns is demanded. <sect> <idx/IOExts/ <label id="sec:IOExts"> <p> This library provides the following extensions to the IO monad: <itemize> <item> The operations <tt/fixIO/, <tt/unsafePerformIO/ and <tt/unsafeInterleaveIO/ described in <cite id="ImperativeFP"> <item> References (aka mutable variables) and mutable arrays (but no form of mutable byte arrays) <item> <tt/performGC/ triggers an immediate garbage collection <item> When called, <tt/trace/ prints the string in its first argument, and then returns the second argument as its result. The <tt/trace/ function is not referentially transparent, and should only be used for debugging, or for monitoring execution. <!-- You should also be warned that, unless you understand some of the details about the way that Haskell programs are executed, results obtained using <tt/trace/ can be rather confusing. For example, the messages may not appear in the order that you expect. Even ignoring the output that they produce, adding calls to <tt/trace/ can change the semantics of your program. Consider this a warning! --> <item> <tt/unsafePtrEq/ compares two values for pointer equality without evaluating them. The results are not referentially transparent and may vary significantly from one compiler to another or in the face of semantics-preserving program changes. However, pointer equality is useful in creating a number of referentially transparent constructs such as this simplified memoisation function: <tscreen><verb> > cache :: (a -> b) -> (a -> b) > cache f = \x -> unsafePerformIO (check x) > where > ref = unsafePerformIO (newIORef (error "cache", error "cache")) > check x = readIORef ref >>= \ (x',a) -> > if x `unsafePtrEq` x' then > return a > else > let a = f x in > writeIORef ref (x, a) >> > return a </verb></tscreen> </itemize> <tscreen><verb> module IOExts where fixIO :: (a -> IO a) -> IO a unsafePerformIO :: IO a -> a unsafeInterleaveIO :: IO a -> IO a data IORef a -- mutable variables containing values of type a newIORef :: a -> IO (IORef a) readIORef :: IORef a -> IO a writeIORef :: IORef a -> a -> IO () instance Eq (IORef a) data IOArray ix elt -- mutable arrays indexed by values of type ix -- containing values of type a. newIOArray :: Ix ix => (ix,ix) -> elt -> IO (IOArray ix elt) boundsIOArray :: Ix ix => IOArray ix elt -> (ix, ix) readIOArray :: Ix ix => IOArray ix elt -> ix -> IO elt writeIOArray :: Ix ix => IOArray ix elt -> ix -> elt -> IO () freezeIOArray :: Ix ix => IOArray ix elt -> IO (Array ix elt) instance Eq (IOArray ix elt) performGC :: IO () trace :: String -> a -> a unsafePtrEq :: a -> a -> Bool </verb></tscreen> <!-- <sect> <idx/GlaExts/ <p> This library provides a convenient bundle of most of the extensions available in GHC and Hugs. This module is generally more stable than the other modules of non-standard extensions so you might choose to import them from here rather than going straight to the horses mouth. <tscreen><verb> module GlaExts( module GlaExts, module IOExts, module ST, module Addr ) where import IOExts import ST import Addr trace :: String -> a -> a performGC :: IO () </verb></tscreen> The GHC version also provides the types <tt/PrimIO/, <tt/RealWorld/, <tt/ByteArray/, <tt/Lift/ and operations on these types. It also provides the unboxed views of the types <tt/Int/, <tt/Addr/, <tt/Word/, <tt/Float/, <tt/Double/, <tt/Integer/ and <tt/Char/ and a number of ``primitive operations'' (<tt/+&num/, <tt/plusFloat&num/, etc.). --> <sect> <idx/Bits/ <label id="sec:Bits"> <p> This library defines bitwise operations for signed and unsigned ints. <tscreen><verb> module Bits where infixl 8 `shift`, `rotate` infixl 7 .&. infixl 6 `xor` infixl 5 .|. class Bits a where (.&.), (.|.), xor :: a -> a -> a complement :: a -> a shift :: a -> Int -> a rotate :: a -> Int -> a bit :: Int -> a setBit :: a -> Int -> a clearBit :: a -> Int -> a complementBit :: a -> Int -> a testBit :: a -> Int -> Bool bitSize :: a -> Int isSigned :: a -> Bool shiftL, shiftR :: Bits a => a -> Int -> a rotateL, rotateR :: Bits a => a -> Int -> a shiftL a i = shift a i shiftR a i = shift a (-i) rotateL a i = rotate a i rotateR a i = rotate a (-i) </verb></tscreen> Notes: <itemize> <item> <tt/bitSize/ and <tt/isSigned/ are like <tt/floatRadix/ and <tt/floatDigits/ -- they return parameters of the <em/type/ of their argument rather than of the particular argument they are applied to. <tt/bitSize/ returns the number of bits in the type (or <tt/Nothing/ for unbounded types); and <tt/isSigned/ returns whether the type is signed or not. <item> <tt/shift/ performs sign extension. That is, right shifts fill the top bits with 1 if the number is negative and with 0 otherwise. (Since unsigned types are always positive, the top bit is always filled with 0.) <item> Bits are numbered from 0 with bit 0 being the least significant bit. <item> <tt/shift x i/ and <tt/rotate x i/ shift to the left if <tt/i/ is positive and to the right otherwise. <!-- <item> <tt/rotate/ is well defined only if bitSize returns a number. (Maybe we should impose a Bounded constraint on it?) --> <item> <tt/bit i/ is the value with the i'th bit set. </itemize> <sect> <idx/Word/ <label id="sec:Word"> <p> This library provides unsigned integers of various sizes. The types supported are as follows: <tabular ca="ll"> type | number of bits @ <hline> Word8 | 8 @ Word16 | 16 @ Word32 | 32 @ Word64 | 64 @ <hline> </tabular> For each type <it/W/ above, we provide the following functions and instances. The type <it/I/ refers to the signed integer type of the same size. <tscreen><verb> data W -- Unsigned Ints instance Eq W instance Ord W instance Show W instance Read W instance Bounded W instance Num W instance Real W instance Integral W instance Enum W instance Ix W instance Bits W </verb></tscreen> Plus <tscreen><verb> word8ToWord32 :: Word8 -> Word32 word32ToWord8 :: Word32 -> Word8 word16ToWord32 :: Word16 -> Word32 word32ToWord16 :: Word32 -> Word16 word8ToInt :: Word8 -> Int intToWord8 :: Int -> Word8 word16ToInt :: Word16 -> Int intToWord16 :: Int -> Word16 word32ToInt :: Word32 -> Int intToWord32 :: Int -> Word32 </verb></tscreen> Notes: <itemize> <item> All arithmetic is performed modulo 2^n One non-obvious consequequence of this is that <tt/negate/ should <em/not/ raise an error on negative arguments. <item> The coercion <tt/wToI/ converts an unsigned n-bit value to the signed n-bit value with the same representation. For example, <tt/word8ToInt8 0xff = -1/. Likewise, <tt/iToW/ converts signed n-bit values to the corresponding unsigned n-bit value. <item> ToDo: complete the set of coercion functions. <item> Use <tt/Prelude.fromIntegral :: (Integral a, Num b) => a -> b/ to coerce between different sizes or to preserve sign when converting between values of the same size. <item> It would be very natural to add a type a type <tt/Natural/ providing an unbounded size unsigned integer --- just as <tt/Integer/ provides unbounded size signed integers. We do not do that yet since there is no demand for it. Doing so would require <tt/Bits.bitSize/ to return <tt/Maybe Int/. <item> The <tt/Enum/ instances stop when they reach their upper or lower bound --- they don't overflow the way the <tt/Int/ and <tt/Float/ instances do. <item> It would be useful to provide a function (or a family of functions?) which coerced between any two Word types (without going through Integer). </itemize> Hugs only provides <tt/Eq/, <tt/Ord/, <tt/Read/ and <tt/Show/ instances for <tt/Word64/ at the moment. <sect> <idx/Int/ <label id="sec:Int"> <p> This library provides signed integers of various sizes. The types supported are as follows: <tabular ca="ll"> type | number of bits @ <hline> Int8 | 8 @ Int16 | 16 @ Int32 | 32 @ Int64 | 64 @ <hline> </tabular> For each type <it/I/ above, we provide the following instances. <tscreen><verb> data I -- Signed Ints iToInt :: I -> Int -- not provided for Int64 intToi :: Int -> I -- not provided for Int64 instance Eq I instance Ord I instance Show I instance Read I instance Bounded I instance Num I instance Real I instance Integral I instance Enum I instance Ix I instance Bits I </verb></tscreen> Plus <tscreen><verb> int8ToInt :: Int8 -> Int intToInt8 :: Int -> Int8 int16ToInt :: Int16 -> Int intToInt16 :: Int -> Int16 int32ToInt :: Int32 -> Int intToInt32 :: Int -> Int32 </verb></tscreen> <itemize> <item> Hugs does not provide <tt/Int64/ at the moment. <item> ToDo: complete the set of coercion functions. </itemize> <sect> <idx/Addr/ <label id="sec:Addr"> <p> This library provides machine addresses and is primarily intended for use in creating foreign function interfaces using GreenCard. <tscreen><verb> module Addr where data Addr -- Address type instance Eq Addr nullAddr :: Addr plusAddr :: Addr -> Int -> Addr -- read value out of _immutable_ memory indexCharOffAddr :: Addr -> Int -> Char indexIntOffAddr :: Addr -> Int -> Int -- should we drop this? indexAddrOffAddr :: Addr -> Int -> Addr indexFloatOffAddr :: Addr -> Int -> Float indexDoubleOffAddr :: Addr -> Int -> Double indexWord8OffAddr :: Addr -> Int -> Word8 indexWord16OffAddr :: Addr -> Int -> Word16 indexWord32OffAddr :: Addr -> Int -> Word32 indexWord64OffAddr :: Addr -> Int -> Word64 indexInt8OffAddr :: Addr -> Int -> Int8 indexInt16OffAddr :: Addr -> Int -> Int16 indexInt32OffAddr :: Addr -> Int -> Int32 indexInt64OffAddr :: Addr -> Int -> Int64 -- read value out of mutable memory readCharOffAddr :: Addr -> Int -> IO Char readIntOffAddr :: Addr -> Int -> IO Int -- should we drop this? readAddrOffAddr :: Addr -> Int -> IO Addr readFloatOffAddr :: Addr -> Int -> IO Float readDoubleOffAddr :: Addr -> Int -> IO Double readWord8OffAddr :: Addr -> Int -> IO Word8 readWord16OffAddr :: Addr -> Int -> IO Word16 readWord32OffAddr :: Addr -> Int -> IO Word32 readWord64OffAddr :: Addr -> Int -> IO Word64 readInt8OffAddr :: Addr -> Int -> IO Int8 readInt16OffAddr :: Addr -> Int -> IO Int16 readInt32OffAddr :: Addr -> Int -> IO Int32 readInt64OffAddr :: Addr -> Int -> IO Int64 -- write value into mutable memory writeCharOffAddr :: Addr -> Int -> Char -> IO () writeIntOffAddr :: Addr -> Int -> Int -> IO () -- should we drop this? writeAddrOffAddr :: Addr -> Int -> Addr -> IO () writeFloatOffAddr :: Addr -> Int -> Float -> IO () writeDoubleOffAddr :: Addr -> Int -> Double -> IO () writeWord8OffAddr :: Addr -> Int -> Word8 -> IO () writeWord16OffAddr :: Addr -> Int -> Word16 -> IO () writeWord32OffAddr :: Addr -> Int -> Word32 -> IO () writeWord64OffAddr :: Addr -> Int -> Word64 -> IO () writeInt8OffAddr :: Addr -> Int -> Int8 -> IO () writeInt16OffAddr :: Addr -> Int -> Int16 -> IO () writeInt32OffAddr :: Addr -> Int -> Int32 -> IO () writeInt64OffAddr :: Addr -> Int -> Int64 -> IO () </verb></tscreen> Hugs provides <tt/Addr/ and <tt/nullAddr/ but does not provide any of the index, read or write functions. They can be implemented using GreenCard if required. <sect> <idx/ForeignObj/ <label id="sec:ForeignObj"> <p> This module is provided by GHC but not by Hugs. GreenCard for Hugs provides the <tt/ForeignObj/ type. <sect> <idx/Concurrent/ <label id="sec:Concurrent"> <p> This library provides the Concurrent Haskell extensions <cite id="concurrentHaskell:popl96">. We are grateful to the Glasgow Haskell Project for allowing us to redistribute their implementation of this module. <tscreen><verb> module Concurrent where data ThreadId -- thread identifiers instance Eq ThreadId instance Ord ThreadId forkIO :: IO () -> IO ThreadId killThread :: ThreadId -> IO () data MVar a -- Synchronisation variables newEmptyMVar :: IO (MVar a) newMVar :: a -> IO (MVar a) takeMVar :: MVar a -> IO a putMVar :: MVar a -> a -> IO () swapMVar :: MVar a -> a -> IO a readMVar :: MVar a -> IO a instance Eq (MVar a) data Chan a -- channels newChan :: IO (Chan a) writeChan :: Chan a -> a -> IO () readChan :: Chan a -> IO a dupChan :: Chan a -> IO (Chan a) unReadChan :: Chan a -> a -> IO () getChanContents :: Chan a -> IO [a] writeList2Chan :: Chan a -> [a] -> IO () data CVar a -- one element channels newCVar :: IO (CVar a) putCVar :: CVar a -> a -> IO () getCVar :: CVar a -> IO a data QSem -- General/quantity semaphores newQSem :: Int -> IO QSem waitQSem :: QSem -> IO () signalQSem :: QSem -> IO () data QSemN -- General/quantity semaphores newQSemN :: Int -> IO QSemN waitQSemN :: QSemN -> Int -> IO () signalQSemN :: QSemN -> Int -> IO () type SampleVar a -- Sample variables newEmptySampleVar:: IO (SampleVar a) newSampleVar :: a -> IO (SampleVar a) emptySampleVar :: SampleVar a -> IO () readSampleVar :: SampleVar a -> IO a writeSampleVar :: SampleVar a -> a -> IO () </verb></tscreen> Notes: <itemize> <item> GHC uses preemptive multitasking: Context switches can occur at any time, except if you call a C function (like \verb"getchar") that blocks waiting for input. Hugs uses cooperative multitasking: Context switches only occur when you use one of the primitives defined in this module. This means that programs such as: <tscreen><verb> main = forkIO (write 'a') >> write 'b' where write c = putChar c >> write c </verb></tscreen> will print either <tt/aaaaaaaaaaaaaa.../ or <tt/bbbbbbbbbbbb.../, instead of some random interleaving of <tt/a/s and <tt/b/s. In practice, cooperative multitasking is sufficient for writing simple graphical user interfaces. <item> Hugs does not provide the functions <tt/mergeIO/ or <tt/nmergeIO/ since these require preemptive multitasking. <item> Thread identities and <tt/killThread/ has not been implemented yet on either system. The plan is that <tt/killThread/ will raise an IO exception in the killed thread which it can catch --- perhaps allowing --> --it to kill its children before exiting. <item> The <tt/Ord/ instance for <tt/ThreadId/s provides an arbitrary total ordering which might be used to build an ordered binary tree, say. </itemize> <sect> <idx/Pretty/ <label id="sec:Pretty"> <p> This library contains Simon Peyton Jones' implementation of John Hughes's pretty printer combinators. <tscreen><verb> module Pretty where infixl 6 <> infixl 6 <+> infixl 5 $$, $+$ data Doc -- the Document datatype -- The primitive Doc values empty :: Doc text :: String -> Doc char :: Char -> Doc int :: Int -> Doc integer :: Integer -> Doc float :: Float -> Doc double :: Double -> Doc rational :: Rational -> Doc semi, comma, colon, space, equals :: Doc lparen, rparen, lbrack, rbrack, lbrace, rbrace :: Doc parens, brackets, braces :: Doc -> Doc quotes, doubleQuotes :: Doc -> Doc -- Combining Doc values (<>) :: Doc -> Doc -> Doc -- Beside hcat :: [Doc] -> Doc -- List version of <> (<+>) :: Doc -> Doc -> Doc -- Beside, separated by space hsep :: [Doc] -> Doc -- List version of <+> ($$) :: Doc -> Doc -> Doc -- Above; if there is no -- overlap it "dovetails" the two vcat :: [Doc] -> Doc -- List version of $$ cat :: [Doc] -> Doc -- Either hcat or vcat sep :: [Doc] -> Doc -- Either hsep or vcat fcat :: [Doc] -> Doc -- ``Paragraph fill'' version of cat fsep :: [Doc] -> Doc -- ``Paragraph fill'' version of sep nest :: Int -> Doc -> Doc -- Nested hang :: Doc -> Int -> Doc -> Doc punctuate :: Doc -> [Doc] -> [Doc] -- punctuate p [d1, ... dn] = [d1 <> p, d2 <> p, ... dn-1 <> p, dn] -- Displaying Doc values instance Show Doc render :: Doc -> String -- Uses default style renderStyle :: Style -> Doc -> String data Style = Style { lineLength :: Int, -- In chars ribbonsPerLine :: Float, -- Ratio of ribbon length -- to line length mode :: Mode } data Mode = PageMode -- Normal | ZigZagMode -- With zig-zag cuts | LeftMode -- No indentation, infinitely long lines | OneLineMode -- All on one line </verb></tscreen> <biblio files="refs" style="abbrv"> </article>