imz*_*hev 10 haskell mutable ffi state-monad
我想通过这样的设置有接口操纵从FFI某种类型的结构STArray或STRef在ST单子.我会和供种作为此结构(如有用的操作的理解名字我自己的具体方法readArray和writeArray数组).
实现这个的最简单方法是什么?
STArray(基于https://hackage.haskell.org/package/base-4.7.0.2/docs/src/GHC-Arr.html)的实现对于那些不了解用于此目的的特殊GHC技术的人来说看起来太复杂了. .
我可以在一个更简单,可理解的Haskell级别上写一些东西吗?
我不是在询问如何通过FFI访问结构.
我宁愿写getter和setter函数在C,我想反映他们在Haskell(获得ST-操作,如readArray和writeArray).
如果我没有弄错的话,我可以将外来函数声明为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)
Run Code Online (Sandbox Code Playgroud)
因此,出现了这样的想法:"效果"和"纯粹"之间的中间形式是可能的,以节省程序员的工作."效果"限于"有限状态":
foreign import ccall safe "getValue.h writeValue" writeValue :: (ValueRef s) -> Value -> ST s () -- modeled after writeSTRef
Run Code Online (Sandbox Code Playgroud)
除了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!
Run Code Online (Sandbox Code Playgroud)
我只是无法获得有关ValueRef正确的详细信息:如果我们定义这个非参数化类型,那么编译器如何使用参数化的类型来给出ST动作?
可能这在GHC中不可用,但可能是有用的扩展,不是吗?
use*_*038 11
根据我的评论,我将简要说明如何做到这一点.
首先从基本的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 ) ;
Run Code Online (Sandbox Code Playgroud)
然后是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 }
Run Code Online (Sandbox Code Playgroud)
而且你所有的外国进口商品.
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 ()
Run Code Online (Sandbox Code Playgroud)
如果你需要在C方面做一些特别的事情,但又不想强迫你的用户打电话free给你Foo,你可以ForeignPtr在你的实际代表中使用a .
data STFoo s = STFoo (ForeignPtr Foo)
Run Code Online (Sandbox Code Playgroud)
当然,这种类型必须是抽象的.如果您使用的是GHC 7.8或更高版本,则还应包括
{-# LANGUAGE RoleAnnotations #-} -- at the top
type role STFoo nominal
Run Code Online (Sandbox Code Playgroud)
或者人们可以打破你从中得到的不变量ST.当你创建一个新的时STFoo,你想把C侧终结器放在它上面.
newFoo :: ST s (STFoo s)
newFoo = STFoo <$> unsafeIOToST (c_newFoo >>= newForeignPtr p_freeFoo)
Run Code Online (Sandbox Code Playgroud)
阅读和写作基本上只是一些强制.
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)
Run Code Online (Sandbox Code Playgroud)
您还可以获得Haskell端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)
Run Code Online (Sandbox Code Playgroud)
这一切都伴随着警告,如果你的C函数不是异常安全或违反参照透明度,Haskell类型系统将无法帮助你,你可能最终暴露unsafePerformIO.