let makeSealerPair nullObj = let content = ref nullObj in let seal obj = let box () = content := obj in box in let unseal filledbox = content := nullObj; filledbox (); let result = !content in content := nullObj; result in (seal, unseal) ;;Here is a simple test—demo:
let seal, unseal = makeSealerPair "Zilch";; unseal (seal "Hello");; let s1, u1 = makeSealerPair 34;; let s2, u2 = makeSealerPair 34;; u1 (s2 11);; => 34OCaml deduces that the type of makeSealerPair is:
This nice code needs careful commentary. Each invocation of makeSealerPair returns a matched pair of functions called the sealer and unsealer. Invoking a sealer with argument zot yields a box with zot inside. Invoking the matched unsealer passing that box returns zot.
The identifier content sounds like it refers to zot. It does not except for a brief moment as the unsealer is invoked. This code is not thread safe. A given matched pair is good only for one type and that is the type of the argument to makeSealerPair—a kosher pair.
The code for unseal stores nullObj into the cell content twice for reasons that are slightly obscure.
There is an obscure ‘burden’ placed upon legitimate holders of these boxes. One must not invoke them lest certain attacks be enabled! How to express this?? It is not a burden on the code which gains no legitimate function by invoking a box. It places a burden on the programmer in understanding the warning.
This plan is incompatible with preemptive scheduling since there is one mutable cell, content which is shared by boxes from the same matched sealer unsealer pair. Platforms that survive time hogs must preempt, I presume. That enables attacks where threads of code that mutate may be interleaved.
let makeSealerPair nullObj = let content = ref nullObj in let seal obj = let box () = content := obj in box and unseal filledbox = content := nullObj; filledbox (); let result = !content in content := nullObj; result in (seal, unseal);;Slightly smaller and obscurer:
let makeSealerPair nullObj = let content = ref nullObj in let seal obj () = content := obj and unseal filledbox = content := nullObj; filledbox (); let result = !content in content := nullObj; result in (seal, unseal);;Worse:
let makeSealerPair nullObj = let content = ref nullObj in (fun obj () -> content := obj), (fun filledbox -> content := nullObj; filledbox (); let result = !content in content := nullObj; result);;
See this! I have always been queasy about weak maps but I can’t pin down any problems with them.