Обычно я решаю эту проблему, передавая обратные вызовы в качестве указателей на функции. Например, у меня есть приложение, в котором нажатие кнопки требует обратного вызова в Haskell (я использую Какао, но, за исключением имен, он очень похож).
Сначала я создаю подкласс объекта NSButton и предоставляю моему новому классу ButtonC закрытый член типа void(*onClickCallback)()
. Я также объявляю функцию void setClickCallback(ButtonC *button, void(*callback)());
Реализация вашего подкласса, чтобы при каждом нажатии кнопки вызывался указатель функции. В .Net может быть разумный способ сделать это с делегатами (давно я не использовал .Net).
Далее моя привязка на Haskell выглядит следующим образом (некоторый код опущен):
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 = ...
Теперь данные на Haskell типа IO ()
можно обернуть в FunPtr
и передать в GUI с помощью buttonSetCallback
. При каждом нажатии кнопки выполняется действие IO ()
.
createButton :: IO ButtonPtr
createButton = do
btn <- buttonNew
let buttonClicked = print "The button was clicked!"
btnCallback <- makeClickCallback buttonClicked
buttonSetCallback btn btnCallback
return btn
С FunPtr
s следует опасаться, что они не являются сборщиком мусора. Вам нужно вручную освободить их, когда вы закончите, иначе у вас будет утечка памяти. Хорошей практикой является никогда не делиться FunPtr
с, а также никогда не сохранять ссылки на них на стороне Хаскеля. Таким образом, ваш объект может освободить FunPtr
как часть его очистки. Это требует еще одного обратного вызова в Haskell (freeFunPtr
функция), который должен быть разделен между всеми вашими объектами и сам по себе освобождается только после завершения вашего исполняемого файла.