| 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 | }}} |