RE: Possible runtime overhead of wrapping the IO monad?

| -----Original Message----- | From: glasgow-haskell-users-bounces@haskell.org [mailto:glasgow-haskell-users- | bounces@haskell.org] On Behalf Of Brian Hulley | Sent: 30 March 2006 13:14 | To: John Meacham; glasgow-haskell-users@haskell.org | Subject: Re: Possible runtime overhead of wrapping the IO monad? | | John Meacham wrote: | > On Thu, Mar 30, 2006 at 03:50:06AM +0100, Brian Hulley wrote: | >> where the intention is that the callback will take the width and | >> height of the window and return a RenderM action, the problem is | >> that because the FFI does not allow RenderM to appear in a foreign | >> type. | > | > it should, the types in foreign declarations should "see through" | > newtypes. | | Unfortunately GHC does not seem to support this: | | foreign import ccall duma_clear :: Word.Word32 -> RenderM () | | Unacceptable result type in foreign declaration: RenderM () | When checking declaration: | foreign import ccall safe "static &duma_clear" | duma_clear :: GHC.Word.Word32 -> RenderM () | | even though the FFI spec agrees with you (Section 3.2): | | The argument types ati produced by fatype must be | marshallable foreign types; that is, each ati is either (1) | a basic foreign type or (2) a type synonym or renamed | datatype of a marshallable foreign type. Moreover, the | result type rt produced by frtype must be a | marshallable foreign result type; that is, | it is either a marshallable foreign type, ... GHC does unwrap newtype result types, as in foreign import foo :: Int -> IO Boogle newtype Boogle = B Int which is what the manual meant. I'd never thought of newtyping the IO monad itself. Why are you doing that, incidentally? I guess it wouldn't be too hard to unwrap the monad type too. If it's important to you, please submit a feature request. Thanks Simon

Simon Peyton-Jones wrote:
GHC does unwrap newtype result types, as in
foreign import foo :: Int -> IO Boogle newtype Boogle = B Int
which is what the manual meant. I'd never thought of newtyping the IO monad itself. Why are you doing that, incidentally?
I'm designing a simple API for interaction with a graphics window, where a render callback written in Haskell would set up some transforms and draw shapes and text. Using DirectX in C, my code would be something like: void Render(){ Clear(); BeginScene(); DrawPrimitive(); ... EndScene(); } void Keypress(int c){ // deal with user pressing c } For Haskell, I could just make FFI declarations for Clear, BeginScene, etc as follows (modulo capitalization): foreign import ccall clear :: IO () foreign import ccall scene :: IO () -> IO () foreign import ccall drawPrimitive :: IO () type RenderCallback :: IO () type KeypressCallback :: IO () render :: IO () render = do clear scene $ do drawPrimitive So Haskell has allowed the requirement for begin/end bracketing to be neatly abstracted by using a higher order function. However, because everything is just in the IO monad, the following function, which misuses the API, is also well typed: keypress :: IO () keypress = drawPrimitive -- not allowed at this point!!! So the reason for wanting to wrap the IO monad is to make explicit the context in which the API functions can be correctly used as in: newtype RenderM a = RenderM (IO a) deriving (Monad, MonadIO, Functor) newtype DrawM a = DrawM (IO a) deriving (Monad, MonadIO, Functor) ... type RenderCallback :: RenderM () foreign import ccall clear :: RenderM () foreign import ccall scene :: DrawM () -> RenderM () foreign import ccall drawPrimitive :: DrawM () render :: RenderM () render = ... -- as before Now render is well typed but keypress is not, all things that, to use an API correctly in C or C++ you have to just guess from reading documentation but in Haskell with different monads is now explicit so that incorrect usage of the API can be detected by the compiler instead of resulting in weird runtime behaviour. Regards, Brian.

Apologies for the syntax/type errors in my last post! :-) Brian Hulley wrote:
type RenderCallback :: IO () type KeypressCallback :: IO ()
type RenderCallback = IO () type KeypressCallback = Int -> IO ()
keypress :: IO () keypress = drawPrimitive -- not allowed at this point!!!
keypress :: KeypressCallback keypress c = drawPrimitive -- not allowed at this point!!
participants (2)
-
Brian Hulley
-
Simon Peyton-Jones