0

I'm writing a library in Haskell, in which there are parameters that must be initialized, but never changes during execution. For example:

initialize :: CInt -> CPtr CInt -> IO ParameterData

However, ParameterData is a complex datatype that is too expensive to build up every time I need it. How can I set up the program so that when the C code calls the initialization function, it computes a ParameterData and sets it somewhere as a constant that the rest of the Haskell library can access?

Trebor
  • 307
  • 2
  • 10
  • Just construct it at the beginning of the `main`, and then pass it as parameter to the functions that need it. You can not set the outcome of an `IO a` object to a constant (unless with `unsafePerformIO`, but as the name suggests, this is *not* a good idea), since then you implement a *global state* which is something that Haskell explicitly does not want. – Willem Van Onsem Aug 16 '21 at 06:29
  • 1
    @WillemVanOnsem The haskell code acts as a library, should there be a `main` at all? – Trebor Aug 16 '21 at 06:29
  • no, in that case, you make a function to retrieve the `ParameterData`, and you define your functions with a parameter that specifies the `ParameterData`, then it is up to the programmer who uses the library to construct it once, and pass it multiple time.s – Willem Van Onsem Aug 16 '21 at 06:31
  • Another alternative would be to implement a `State` monad, and thus an item that sets the `ParameterData`, but this looks like overengineering. – Willem Van Onsem Aug 16 '21 at 06:32
  • @WillemVanOnsem Is there any reference I can read about passing Haskell values around in C code? – Trebor Aug 16 '21 at 06:32
  • 1
    @Trebor What I’ve done in a similar circumstance is to pass the value back to C as a `StablePointer`; on the C side, this is simply stored in a variable and passed back to Haskell as needed. – bradrn Aug 16 '21 at 06:38
  • @Trebor Code here: [`BrassicaInterop.hs`](https://github.com/bradrn/brassica/blob/14edcefe01ca8a0ea47c09ea783fcaa9ff829fe9/gui/brassica-interop/src/BrassicaInterop.hs). It’s not an exact analogue of your case (there’s much less global state), but you can see how I initialise a `StablePtr (IORef _)` in `initResults` (which somehow ended up with a misplaced type signature, whoops!), and then pass it back into Haskell from C in `parseTokeniseAndApplyRules_hs`. – bradrn Aug 16 '21 at 06:41

1 Answers1

1

To put together the comments.

In the Haskell code, write the initialize function as

initialize :: CInt -> Ptr Int -> IO (StablePtr ParameterData)

and pass the stable pointer to each function (write a wrapper that fetches the contents out if you may). In the C code, store the StablePtr as a global state, and pass it in any Haskell functions that need it.

Trebor
  • 307
  • 2
  • 10