Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a Haskell application with a .NET GUI

Tags:

haskell

cabal

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:

  1. 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

  2. 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?

like image 914
Robert Avatar asked Mar 19 '12 08:03

Robert


1 Answers

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 function void 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):

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 a FunPtr and passed into the GUI with buttonSetCallback. Whenever the button is pressed, the IO () action is performed.

createButton :: IO ButtonPtr
createButton = do
    btn <- buttonNew
    let buttonClicked = print "The button was clicked!"
    btnCallback <- makeClickCallback buttonClicked
    buttonSetCallback btn btnCallback
    return btn

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 share FunPtrs, and also never retain references to them on the Haskell side. That way your object can free the FunPtr as part of its cleanup. This requires yet another callback into Haskell (a freeFunPtr function) that should be shared between all your objects and is itself only freed when your executable terminates.

like image 137
John L Avatar answered Oct 21 '22 22:10

John L