如何使用简单的技术用我自己的底层表示(类似于STRef或STArray)在ST-monad中实现动作?

imz*_*hev 10 haskell mutable ffi state-monad

我想通过这样的设置有接口操纵从FFI某种类型的结构STArraySTRefST单子.我会和供种作为此结构(如有用的操作的理解名字我自己的具体方法readArraywriteArray数组).

实现这个的最简单方法是什么?

STArray(基于https://hackage.haskell.org/package/base-4.7.0.2/docs/src/GHC-Arr.html)的实现对于那些不了解用于此目的的特殊GHC技术的人来说看起来太复杂了. .

我可以在一个更简单,可理解的Haskell级别上写一些东西吗?

备注

我不是在询问如何通过FFI访问结构.

我宁愿写getter和setter函数在C,我想反映他们在Haskell(获得ST-操作,如readArraywriteArray).

关于轻松声明这种界面的一些想法(可能的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)
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.