I usually solve this by passing callbacks as function pointers. For example, I have an application in which a button click needs to be called back to Haskell (I use Cocoa, but with the exception of names that are very similar).
First, I subclass the NSButton object and give the new ButtonC class to a private member of type void(*onClickCallback)() . I also declare the function void setClickCallback(ButtonC *button, void(*callback)()); An implementation of your subclass, so whenever a button is clicked, a function pointer is called. There can be a smart way in .Net to do this with delegates (it's been a while since I used .Net).
Next, my Haskell binding looks like this (code omitted):
module Foreign.Button where data ButtonObj type ButtonPtr = ForeignPtr ButtonObj foreign import ccall unsafe "setClickCallback" c_setClickCallback :: Ptr ButtonObj -> FunPtr (IO ()) -> IO () foreign import ccall "wrapper" makeClickCallback :: IO () -> IO (FunPtr (IO ())) buttonSetCallback :: ButtonPtr -> FunPtr (IO ()) -> IO () buttonSetCallback btn cb = withForeignPtr btn $ \p -> c_setClickCallback p cb buttonNew :: IO ButtonPtr buttonNew = ...
Now, Haskell data of type IO () can be wrapped in FunPtr and passed to the GUI using buttonSetCallback . Whenever a button is pressed, the IO () action is executed.
createButton :: IO ButtonPtr createButton = do btn <- buttonNew let buttonClicked = print "The button was clicked!" btnCallback <- makeClickCallback buttonClicked buttonSetCallback btn btnCallback return btn
Beware of FunPtr so that they do not collect garbage. You need to manually free them when done, or you will have a memory leak. Itβs good practice to never share FunPtr s, and never to keep links to them from Haskell. That way, your object can free FunPtr as part of its cleanup. This requires another callback in Haskell (the freeFunPtr function), which must be shared between all of your objects and released only after the completion of your executable file.
John l
source share