| | 59 | === unsafePerformIO === |
| | 60 | |
| | 61 | The FFI libraries will include the function `unsafePerformIO` (as in the FFI addendum). However, we limit the support use of the function purely to implement memory management and memory access during marshalling for foreign functions that ought to get a pure Haskell type. Specifically, we will use the following wording (or something close): |
| | 62 | |
| | 63 | Wrap a pure computation that uses local memory. The only IO operations permitted in the IO action passed to `unsafePerformIO` are |
| | 64 | (a) local allocation (`alloca`, `allocaBytes` and derived operations such as `withArray` and `withCString`), and |
| | 65 | (b) pointer operations (`Foreign.Storable` and `Foreign.Ptr`) on the pointers to local storage, and |
| | 66 | (c) foreign functions whose only observable effect is to read and/or write the locally allocated memory. |
| | 67 | This primitive enables the packaging of external entities that are pure functions except that they pass arguments and/or results via pointers. It is expected that this operation will be replaced in a future revision of Haskell. |
| | 68 | |
| | 93 | |
| | 94 | As noted above, we expect that `unsafePerformIO` will be replaced in a future revision (that supports rank-2 types) with a function that safely encapsulates the local state. A possible implementation would be the following: |
| | 95 | {{{ |
| | 96 | {-# LANGUAGE RankNTypes, ForeignFunctionInterface, GeneralizedNewtypeDeriving #-} |
| | 97 | |
| | 98 | module Example where |
| | 99 | |
| | 100 | import Foreign.C.Types |
| | 101 | import qualified Foreign.Marshal.Alloc as F |
| | 102 | import qualified Foreign.Ptr as F |
| | 103 | import qualified Foreign.Storable as F |
| | 104 | import System.IO.Unsafe (unsafePerformIO) |
| | 105 | |
| | 106 | -- Monad for memory regions, and type of pointers within such regions. |
| | 107 | -- Ideally all the implementations would be hidden |
| | 108 | |
| | 109 | newtype ST s a = UnsafeIOToST { unsafeSTToIO :: IO a } deriving Monad |
| | 110 | |
| | 111 | newtype STPtr s a = STPtr (F.Ptr a) |
| | 112 | |
| | 113 | runST :: (forall s. ST s a) -> a |
| | 114 | peek :: F.Storable a => STPtr s a -> ST s a |
| | 115 | poke :: F.Storable a => STPtr s a -> a -> ST s () |
| | 116 | alloca :: F.Storable a => (STPtr s a -> ST s b) -> ST s b |
| | 117 | |
| | 118 | -- sample implementations |
| | 119 | runST act = unsafePerformIO (unsafeSTToIO act) |
| | 120 | peek (STPtr p) = UnsafeIOToST (F.peek p) |
| | 121 | poke (STPtr p) v = UnsafeIOToST (F.poke p v) |
| | 122 | alloca f = UnsafeIOToST (F.alloca (unsafeSTToIO . f . STPtr)) |
| | 123 | |
| | 124 | -- Example of a wrapping of an otherwise pure function using pass-by-reference |
| | 125 | |
| | 126 | frexp :: Double -> (Double, Int) |
| | 127 | frexp x = runST (alloca $ \ exp_ptr -> do |
| | 128 | fraction <- c_frexp (realToFrac x) exp_ptr |
| | 129 | exponent <- peek exp_ptr |
| | 130 | return (realToFrac fraction, fromIntegral exponent)) |
| | 131 | |
| | 132 | foreign import ccall "math.h frexp" |
| | 133 | c_frexp :: CDouble -> STPtr s CInt -> ST s CDouble |
| | 134 | }}} |