I’d like to create a Haskell app with a .NET gui. I’d like to use cabal as my build tool to take advantage of it’s package management etc. I think the Haskell part should be the executable that calls into the .NET code as:
-
This avoids having to manually initialize the Haskell RTC as described here: http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/win32-dlls.html
-
cabal cannot easily produce Windows dlls: http://www.haskell.org/haskellwiki/Cabal/Developer-FAQ#Building_DLLs__with_Cabal
I’ve found it fairly easy to create a Haskell executable that call’s .NET using hs-dotnet, but I also need my GUI code to call back into Haskell. I was hoping to achieve this using Haskell’s “foreign export” command, then call this exported function via .NET native interop. However the “foreign export” function doesn’t seem to create an entry point in the executable, I can’t see the entry point when I do dumpbin /EXPORTS on the resulting executable. I’m not sure whether this is because GHC only create’s entry point’s when creating a dll via the -shared switch or whether cabal add a flag that suppresses entry point creation.
So I guess the question is how do I force GHC to create entry points in my Windows executable? Or would I be better using a .NET executable going though the necessary steps to create a Haskell dll with cabal and manually initializing the Haskell RTC?
I usually solve this by passing callbacks as function pointers. For example, I have an app where pressing a button needs to call back into Haskell (I’m using Cocoa, but except for the names it’s very similar).
First, I subclass an NSButton object, and give my new ButtonC class a private member of type
void(*onClickCallback)(). I also declare a functionvoid setClickCallback(ButtonC *button, void(*callback)());Implement your subclass so that whenever the button is clicked, the function pointer is called. In .Net, there may be a clever way to do this with delegates (it’s been a while since I’ve used .Net).Next, my Haskell binding looks like this (some code omitted):
Now Haskell data of type
IO ()can be wrapped in aFunPtrand passed into the GUI withbuttonSetCallback. Whenever the button is pressed, theIO ()action is performed.One thing to beware of with
FunPtrs is that they aren’t garbage collected. You need to manually deallocate them when you’ve finished or you’ll have a memory leak. A good practice is to never shareFunPtrs, and also never retain references to them on the Haskell side. That way your object can free theFunPtras part of its cleanup. This requires yet another callback into Haskell (afreeFunPtrfunction) that should be shared between all your objects and is itself only freed when your executable terminates.