{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreifv2er2f23z54lrl3balxzm7j2q5eyjqpjfgof43uvj2ijocrk3aq",
    "uri": "at://did:plc:pi6woz4d47bkuws673w2il2r/app.bsky.feed.post/3mehmi4rmknb2"
  },
  "path": "/t/botan-bindings-devlog/6855?page=10#post_192",
  "publishedAt": "2026-02-09T20:42:49.000Z",
  "site": "https://discourse.haskell.org",
  "textContent": "# Monday Update\n\nNo meeting notes today, I was the only one in attendance. Last week saw the start of the `botan-low + memalloc` refactor, mostly getting our feet planted beneath us by dissecting the existing `RNG` interface, and discussing what we wanted to preserve and what we wanted to replace. Major points of were the complexity of the `Internal.ByteString`, `Make`, and `Remake` modules, the resulting `mkBindings` as a method of constructing context objects, and a quick review of the `RNG` interface. Later, in the memory thread we presented an in-depth discussion of the new `Memory` interface that will be handling the allocation and object construction moving forwards.\n\nContinuing where we left off:\n\n# A look at `mkBindings` and handling context objects - `BotanRNG` vs `RNG`\n\nOne of the things that I have been mulling over is the necessity of unwrapping and wrapping pointers. The `mkBindings` function is obviously a critical component, it performs a vital duty, so we cannot get rid of it - and yet it is ugly and terrible. If anything could be improved - here it is. So let us look closer to understand why it exists; then we can replace it.\n\n`Botan.Bindings.RNG` defines `BotanRNG` in the following manner:\n\n\n    -- | Opaque RNG struct\n    data {-# CTYPE \"botan/ffi.h\" \"struct botan_rng_struct\" #-} BotanRNGStruct\n\n    -- | Botan RNG object\n    newtype {-# CTYPE \"botan/ffi.h\" \"botan_rng_t\" #-} BotanRNG\n        = MkBotanRNG { runBotanRNG :: Ptr BotanRNGStruct }\n            deriving newtype (Eq, Ord, Storable)\n\n    foreign import capi safe \"botan/ffi.h botan_rng_init\"\n        botan_rng_init\n            :: Ptr BotanRNG\n            -> ConstPtr CChar\n            -> IO CInt\n\n    foreign import capi safe \"botan/ffi.h &botan_rng_destroy\"\n        botan_rng_destroy\n            :: FinalizerPtr BotanRNGStruct\n\n\nThen, `Botan.Low.RNG` defines `RNG` in the following manner:\n\n\n    newtype RNG = MkRNG { getRNGForeignPtr :: ForeignPtr BotanRNGStruct }\n\n    withRNG     :: RNG -> (BotanRNG -> IO a) -> IO a\n    rngDestroy  :: RNG -> IO ()\n    createRNG   :: (Ptr BotanRNG -> IO CInt) -> IO RNG\n    (withRNG, rngDestroy, createRNG)\n        = mkBindings MkBotanRNG (.runBotanRNG) MkRNG (.getRNGForeignPtr) botan_rng_destroy\n\n    rngInit\n        :: RNGType\n        -> IO RNG\n    rngInit = mkCreateObjectCString createRNG botan_rng_init\n\n\nIf we proceed through this step by step (and ignore how awkwards `mkBindings` is), we note that it is not all that complicated, and that we have several reasons for doing what we did. Making improvements here is valuable, because most all Botan context objects follow this pattern, so whatever we do here probably will affect every other module in a similar manner (we do have a high degree of consistency in that regard).\n\n  * We have an opaque Botan struct type signified by an empty data type `BotanRNGStruct` - this makes sense because as an opaque type, we can never actually see an instance of it.\n  * We have a newtype wrapper around a `Ptr` to a `BotanRNGStruct` - we can actually have an instance of this, it is just a pointer to an opaque struct.\n  * The empty `data BotanRNGStruct` and `newtype BotanRNG` are necessary in order to use `CApiFFI` and `CTYPE`, due to type safety requirements.\n  * This requires boilerplate / wrapping / unwrapping to use the pointer, but is a zero-cost abstraction\n  * The `Ptr` is unmanaged, and we must allocate and provide one to be filled\n  * There is a `FinalizerPtr` in the form of `botan_rng_destroy`, but `Ptr` needs to be a `ForeignPtr` to use it\n    * Maybe we should have both `botan_rng_destroy` and `botan_rng_destroy_finalizer`\n  * `botan-low` lifts the `BotanRNG` from a `Ptr BotanRNGStruct` to a `ForeignPtr BotanRNGStruct` so we can finalize the memory when no references remain\n    * The `RNG` newtype isn’t strictly necessary, unlike `BotanRNGStruct` and `BotanRNG`; it provides value but _could_ be reduced to a `type` alias.\n    * The Botan API requires that we allocate a temporary pointer `Ptr BotanRNG` (that is, a `Ptr (Ptr BotanRNGStruct)`) for the ‘create’ function to populate\n    * Then, `mkBindings` takes that unmanaged `Ptr` and stuffs it into a `ForeignPtr` for the GC to track\n    * The temporary pointer uses `alloca` so it is good to know we are not leaking that pointer either\n  * `mkBindings` and eg `mkCreateObjectCString` are mostly just thin wrappers for converting to and interacting with the inner foreign pointer\n  * The `mkCreateObjectFoo` functions sort of combine this lifting while also handling initialization arguments, but may no longer provide as much / any convenience due to the new `Memory` classes\n  * We didnt have `Memory.Pointer` last time to handle things, so mkBindings filled the gap\n  * The new `Memory.Memory` and `Memory.Pointer` classes allow us to provide the same functionality more naturally - I can just access the inner pointer using `withMem` (which also replaces the `withFoo` that `mkBindings` generates)\n\n\n\nSo, it turns out that some of the complexity is unavoidable - especially the `BotanRNGStruct` vs `BotanRNG` vs `RNG` stuff. We really do need to unwrap the `Ptr` and pack it into a `ForeignPtr` - to allow it to be tracked by the GC, and because a `ForeignPtr BotanRNG` is a `ForeignPtr (Ptr BotanRNGStruct)` and _not_ a `ForeignPtr BotanRNGStruct`. We could turn `RNG` into a `type` instead of a `newtype` but type safety would suffer and I’d rather take advantage of the new `Memory` classes.\n\nBut, it seems that we should be able to more or less get rid of `mkBindings`, if we don’t really need any of its functions anymore! The new memory classes can handle both allocation (create and destroy) and pointer access (withPtr), so all that complexity can just be tossed out.\n\nThis does mean that our context objects will have to conform to some classes, and the `botan-low` library will have slightly more responsiblities, but I think upkeep will be more tolerable, and as a bonus we’ll be polymorphic over the return type of various functions so we can use anything that conforms to the new memory classes instead of being stuck with `ByteString`. That, I think, makes all this worthwhile.",
  "title": "Botan bindings devlog"
}