如何包装不安全的FFI?(Haskell的)

yon*_*ong 3 haskell ffi unsafe-perform-io

这是一个后续问题,是否有充分的理由使用unsafePerformIO?

所以我们知道

p_sin(double *p) { return sin(*p); }
Run Code Online (Sandbox Code Playgroud)

不安全,不能用unsafePerformIO.

但该p_sin函数仍然是一个数学函数,它以不安全的方式实现的事实是一个实现细节.我们并不完全希望矩阵乘法在IO中,因为它涉及分配临时内存.

我们如何以安全的方式包装这个功能?我们需要锁定,自己分配内存等吗?是否有处理此问题的指南/教程?

luq*_*qui 6

实际上,如果你p_sin从这个答案中加入方式是不安全的,那么它取决于p_sin 不是数学函数,至少不是从数字到数字的函数 - 它取决于当同一指针指向的内存不同时给出不同的答案.所以,从数学上讲,这两个电话之间有一些不同之处; 使用正式的指针模型,我们可能会告诉你.例如

type Ptr = Int
type Heap = [Double]

p_sin :: Heap -> Ptr -> Double
Run Code Online (Sandbox Code Playgroud)

然后C函数相当于

p_sin h p = sin (h !! p)
Run Code Online (Sandbox Code Playgroud)

结果不同的原因是因为一个不同的Heap参数,它在C定义中是未命名但隐含的.

如果在p_sin内部使用临时内存,但不依赖于通过其接口的内存状态,例如

double p_sin(double x) {
    double* y = (double*)malloc(sizeof(double));
    *y = sin(x);
    x = *y;
    free(y);
    return x;
}
Run Code Online (Sandbox Code Playgroud)

那么我们确实有一个实际的数学函数Double -> Double,我们可以

foreign import ccall safe "p_sin" 
    p_sin :: Double -> Double
Run Code Online (Sandbox Code Playgroud)

我们没事.界面中的指针在这里杀死纯度,而不是C函数.

更实际的是,假设你有一个用指针实现的C矩阵乘法函数,因为这就是你在C中建模数组的方法.在这种情况下你可能会扩展抽象边界,所以你的程序会发生一些不安全的事情. ,但它们都会被模块用户隐藏起来.在这种情况下,我建议IO在实现中注释所有不安全的内容,然后unsafePerformIO在将其提供给模块用户之前进行注释.这使杂质的表面积最小化.

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
Run Code Online (Sandbox Code Playgroud)

等等

  • @nulvinge,天哪,你是迂腐的."没有" - >"不多" (2认同)