这是对是否有充分的理由使用 unsafePerformIO 的后续问题?
所以我们知道
p_sin(double *p) { return sin(*p); }
不安全,不能与unsafePerformIO
.
但该p_sin
函数仍然是一个数学函数,它以不安全的方式实现这一事实是一个实现细节。例如,我们并不完全希望矩阵乘法出现在 IO 中,因为它涉及分配临时内存。
我们如何以安全的方式包装这个函数?我们是否需要自己锁定、分配内存等?是否有处理此问题的指南/教程?
这是对是否有充分的理由使用 unsafePerformIO 的后续问题?
所以我们知道
p_sin(double *p) { return sin(*p); }
不安全,不能与unsafePerformIO
.
但该p_sin
函数仍然是一个数学函数,它以不安全的方式实现这一事实是一个实现细节。例如,我们并不完全希望矩阵乘法出现在 IO 中,因为它涉及分配临时内存。
我们如何以安全的方式包装这个函数?我们是否需要自己锁定、分配内存等?是否有处理此问题的指南/教程?
实际上,如果您合并该p_sin
答案不安全的方式,则取决于它p_sin
不是数学函数,至少不是从数字到数字的函数-它取决于当相同指针指向的内存不同时给出不同的答案。因此,从数学上讲,这两个调用之间存在一些不同。有了一个正式的指针模型,我们也许可以知道。例如
type Ptr = Int
type Heap = [Double]
p_sin :: Heap -> Ptr -> Double
然后C函数将等同于
p_sin h p = sin (h !! p)
结果不同的原因是由于不同的Heap
参数,该参数未命名但隐含在 C 定义中。
如果p_sin
在内部使用临时内存,但不依赖于通过其接口的内存状态,例如
double p_sin(double x) {
double* y = (double*)malloc(sizeof(double));
*y = sin(x);
x = *y;
free(y);
return x;
}
那么我们确实有一个实际的数学函数Double -> Double
,我们可以
foreign import ccall safe "p_sin"
p_sin :: Double -> Double
我们很好。接口中的指针在这里扼杀了纯度,而不是 C 函数。
更实际地,假设您有一个用指针实现的 C 矩阵乘法函数,因为这就是您在 C 中建模数组的方式。在这种情况下,您可能会扩展抽象边界,因此您的程序中会发生一些不安全的事情,但它们都会对模块用户隐藏。在这种情况下,我建议在您的实现中注释所有不安全的内容IO
,然后unsafePerformIO
在将其提供给模块用户之前立即进行 ing。这使杂质的表面积最小化。
module Matrix
-- only export things guaranteed to interact together purely
(Matrix, makeMatrix, multMatrix)
where
newtype Matrix = Matrix (Ptr Double)
makeMatrix :: [[Double]] -> Matrix
makeMatrix = unsafePerformIO $ ...
foreign import ccall safe "multMatrix"
multMatrix_ :: Ptr Double -> IO (Ptr Double)
multMatrix :: Matrix -> Matrix
multMatrix (Matrix p) = unsafePerformIO $ multMatrix_ p
等等