This is a followup question to Is there ever a good reason to use unsafePerformIO?
So we know that
p_sin(double *p) { return sin(*p); }
is unsafe, and cannot be used with unsafePerformIO
.
But the p_sin
function is still a mathematical function, the fact that it was implemented in an unsafe way is an implementation detail. We don't exactly want, say, matrix multiplication to be in IO just because it involves allocating temporary memory.
How can we wrap this function in a safe way? Do we need to lock, allocate memory ourselves, etc? Is there a guide/tutorial for dealing with this?
Actually, if you incorporate the way
p_sin
is unsafe from that answer, it depends onp_sin
not being a mathematical function, at least not one from numbers to numbers -- it depends on giving different answers when the memory the same pointer points to is different. So, mathematically speaking, there is something different between the two calls; with a formal model of pointers we might be able to tell. E.g.and then the C function would be equivalent to
The reason the results would differ is because of a different
Heap
argument, which is unnamed but implicit in the C definition.If
p_sin
used temporary memory internally, but did not depend on the state of memory through its interface, e.g.then we do have an actual mathematical function
Double -> Double
, and we canand we're be fine. Pointers in the interface are killing the purity here, not C functions.
More practically, let's say you have a C matrix multiplication function implemented with pointers, since that's how you model arrays in C. In this case you'd probably expand the abstraction boundary, so there would be a few unsafe things going on in your program, but they would all be hidden from the module user. In this case, I recommend annotating everything unsafe with
IO
in your implementation, and thenunsafePerformIO
ing right before you give it to the module user. This minimizes the surface area of impurity.etc.