Hey @Tim-ats-d, thanks for the question. The idea behind what to do here is to thread through a unique reference to the key. In an ideal world, here’s what I’d suggest you to write:
open Basement
let increment ~access counter =
let c = Capsule.Data.unwrap ~access counter in
incr c
;;
let f par =
let (P key) = Capsule.create () in
let counter = Capsule.Data.create (fun () -> ref 0) in
let #(key, _) =
Parallel.fork_join2
par
(fun _ ->
let #(_, key) =
Capsule.Key.access key ~f:(fun access -> increment ~access counter)
in
key)
(fun _ -> ())
in
let _ : _ = Capsule.Key.access key ~f:(fun access -> increment ~access counter) in
()
;;
In the future, we expect you’ll be able to write this. But because we don’t yet have layout polymorphism or mode polymorphism (these are features we’re currently working on), you’ll run into some errors. This is the sort of thing that we currently use ppx_template for. But alas, when we wrote the Parallel interface, we didn’t template the functions in it. So we’ll need to do some annoying workarounds.
The first issue is that key has layout void, but Parallel.fork_join2 expects the value you return to have layout value. we can get around this by defining a type with layout value that contains a key within it, like so:
type 'k key_container = K of 'k Capsule.Key.t
The second issue is that Parallel.fork_join2’s return value is always aliased, so we’re essentially forgetting that the key it returns is unique. One way to work around this is to use the module Once from the library unique (interface here). This allows us to perform a dynamic check that we have a unique reference to some value whose mode is aliased.
Altogether, this looks like:
open Basement
let increment ~access counter =
let c = Capsule.Data.unwrap ~access counter in
incr c
;;
type 'k key_container = K of 'k Capsule.Key.t
let f par =
let (P key) = Capsule.create () in
let counter = Capsule.Data.create (fun () -> ref 0) in
let #(key, _) =
Parallel.fork_join2
par
(fun _ ->
let #(_, key) =
Capsule.Key.access key ~f:(fun access -> increment ~access counter)
in
Unique.Once.make (K key))
(fun _ -> ())
in
(* This will raise an exception if it's not unique *)
let (K key) = Unique.Once.get_exn key in
let _ : _ = Capsule.Key.access key ~f:(fun access -> increment ~access counter) in
()
;;
I’d also like to point out that there’s a higher-level capsule API defined in the library portable (interface here), which is more approriate for most use cases. In particular, have a look at Capsule.Isolated, which is convenient when you have a unique reference to a capsule that contains some data.