Creating a Haskell Application Using the .NET GUI

I would like to create a Haskell application with .NET gui. I would like to use cabal as my build tool, to use its package management, etc. I think the Haskell part should be executable, which calls into .NET code like:

It was pretty easy for me to create a Haskell executable that calls .NET using hs-dotnet , but I also need my GUI to call Haskell back. I was hoping to achieve this using the Haskell "Foreign Export" command, and then this exported function is called via the built-in interop.NET. However, the "export foreign export" function does not seem to create an entry point to the executable file, I do not see the entry point when I do dumpbin /EXPORTS in the resulting executable file. I'm not sure if this is because the GHC only creates an entry point when creating a dll using the -shared switch -shared or does Kabbalah add a flag that suppresses the creation of an entry point.

So, I think the question is, how do I get the GHC to create entry points for the Windows executable? Or would I rather use the .NET executable, at least the necessary steps to create a Haskell DLL using cabal and manually initialize the Haskell RTC?

+7
source share
1 answer

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.

+4
source

All Articles