我希望通过与STArray或STRef在ST monad中提供的接口操作FFI中的某种类型的结构。我将有自己的特定方法,为这种结构(比如readArray和writeArray )提供对此结构有用的操作类型的可以理解的名称。
实现这一点的最简单方法是什么?
对于那些不知道一些特殊的STArray技术的人来说,https://hackage.haskell.org/package/base-4.7.0.2/docs/src/GHC-Arr.html(基于https://hackage.haskell.org/package/base-4.7.0.2/docs/src/GHC-Arr.html)的实现看起来太复杂了。
我能在更简单易懂的Haskell水平上写点什么吗?
备注
我不是在问如何通过FFI访问结构。
我宁愿用C编写getter和setter函数,并且我想在Haskell中反映它们(以获得ST-操作,如readArray和writeArray)。
关于这种接口的简单声明(可能的GHC扩展?)的一些想法。
如果我没有弄错,我可以将一个外部函数声明为IO操作或纯函数(如果我确定它是纯的)。我将后者理解为简单地将其包装在unsafePerformIO中
foreign import ccall safe "getValue.h getValue" effect :: CInt -> Ptr CChar
foreign import ccall safe "getValue.h getValue" pure :: CInt -> IO (Ptr CChar)因此,出现了一个介于“效果”和“纯”之间的中间形式,以节省程序员的工作。仅限于“有限国家”的“效果”:
foreign import ccall safe "getValue.h writeValue" writeValue :: (ValueRef s) -> Value -> ST s () -- modeled after writeSTRef除了GHC中这一功能的两个标准变体外,还包括:
foreign import ccall safe "getValue.h writeValue" writeValue :: ValueRef -> Value -> IO ()
foreign import ccall safe "getValue.h writeValue" writeValue :: ValueRef -> Value -> () -- must be really bad!我无法正确地了解有关ValueRef的详细信息:如果我们定义这种非参数化类型,那么编译器如何使用参数化类型来提供ST操作?
也许,这在GHC中是不可用的,但可能是有用的扩展,不是吗?
发布于 2015-02-27 16:47:45
根据我的评论,我将给出一个简短的例子,说明如何做到这一点。
首先从您的基本C模块开始。
typedef struct { int bar; int baz; } foo ;
foo * newFoo ();
void freeFoo (foo * ) ;
int readBar ( foo * ) ;
int readBaz ( foo * ) ;
void writeBar ( foo * , int ) ;
void writeBaz ( foo * , int ) ;然后是Haskell文件。
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C
import Control.Monad.ST
import Control.Monad.ST.Unsafe
import Foreign.Ptr
import Foreign.ForeignPtr
import Control.Applicative
data Foo = Foo { bar :: Int, baz :: Int } 还有你们所有的外国进口品。
foreign import ccall "newFoo" c_newFoo :: IO (Ptr Foo)
foreign import ccall "&freeFoo" p_freeFoo :: FunPtr (Ptr Foo -> IO ())
foreign import ccall "readBar" c_readBar :: Ptr Foo -> IO CInt
foreign import ccall "readBaz" c_readBaz :: Ptr Foo -> IO CInt
foreign import ccall "writeBar" c_writeBar :: Ptr Foo -> CInt -> IO ()
foreign import ccall "writeBaz" c_writeBaz :: Ptr Foo -> CInt -> IO ()如果您需要在C端做一些特殊的事情,但不想强迫您的用户在您的free上调用Foo,您可以在实际的表示中使用ForeignPtr。
data STFoo s = STFoo (ForeignPtr Foo) 当然,这种类型必须是抽象的。如果您正在使用GHC 7.8或更高版本,您还应该包括
{-# LANGUAGE RoleAnnotations #-} -- at the top
type role STFoo nominal 或者人们可以打破你从ST中得到的不变量。当您创建一个新的STFoo时,您希望将C端终结器放在上面。
newFoo :: ST s (STFoo s)
newFoo = STFoo <$> unsafeIOToST (c_newFoo >>= newForeignPtr p_freeFoo)读和写基本上只是一些矫顽力。
readBar :: STFoo s -> ST s Int
readBar (STFoo x) = fromIntegral <$> unsafeIOToST (withForeignPtr x c_readBar)
writeBar :: STFoo s -> Int -> ST s ()
writeBar (STFoo x) i = unsafeIOToST $ withForeignPtr x $ \p ->
c_writeBar p (fromIntegral i) 您还可以获得Haskell side Foo值,这很可能是ST内部计算的结果。这就像freezeSTArray。
freezeFoo :: STFoo s -> ST s Foo
freezeFoo (STFoo x) = unsafeIOToST $ withForeignPtr x $ \p -> do
bar <- fromIntegral <$> c_readBar p
baz <- fromIntegral <$> c_readBaz p
return (Foo bar baz) 这一切都附带了一个警告:如果您的C函数不是异常安全的,或者违反了引用透明度,那么Haskell类型的系统将不会帮助您,您可能最终会暴露unsafePerformIO。
https://stackoverflow.com/questions/28768904
复制相似问题