首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用简单的技术在ST-monad中使用我自己的底层表示(类似于STRef或STArray)实现操作?

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

Stack Overflow用户
提问于 2015-02-27 15:44:09
回答 1查看 315关注 0票数 10

我希望通过与STArraySTRefST monad中提供的接口操作FFI中的某种类型的结构。我将有自己的特定方法,为这种结构(比如readArraywriteArray )提供对此结构有用的操作类型的可以理解的名称。

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

对于那些不知道一些特殊的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-操作,如readArraywriteArray)。

关于这种接口的简单声明(可能的GHC扩展?)的一些想法。

如果我没有弄错,我可以将一个外部函数声明为IO操作或纯函数(如果我确定它是纯的)。我将后者理解为简单地将其包装在unsafePerformIO

代码语言:javascript
复制
foreign import ccall safe "getValue.h getValue" effect :: CInt -> Ptr CChar
foreign import ccall safe "getValue.h getValue" pure :: CInt -> IO (Ptr CChar)

因此,出现了一个介于“效果”和“纯”之间的中间形式,以节省程序员的工作。仅限于“有限国家”的“效果”:

代码语言:javascript
复制
foreign import ccall safe "getValue.h writeValue" writeValue :: (ValueRef s) -> Value -> ST s () -- modeled after writeSTRef

除了GHC中这一功能的两个标准变体外,还包括:

代码语言:javascript
复制
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中是不可用的,但可能是有用的扩展,不是吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-02-27 16:47:45

根据我的评论,我将给出一个简短的例子,说明如何做到这一点。

首先从您的基本C模块开始。

代码语言:javascript
复制
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文件。

代码语言:javascript
复制
{-# 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 } 

还有你们所有的外国进口品。

代码语言:javascript
复制
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

代码语言:javascript
复制
data STFoo s = STFoo (ForeignPtr Foo) 

当然,这种类型必须是抽象的。如果您正在使用GHC 7.8或更高版本,您还应该包括

代码语言:javascript
复制
{-# LANGUAGE RoleAnnotations #-} -- at the top

type role STFoo nominal 

或者人们可以打破你从ST中得到的不变量。当您创建一个新的STFoo时,您希望将C端终结器放在上面。

代码语言:javascript
复制
newFoo :: ST s (STFoo s) 
newFoo = STFoo <$> unsafeIOToST (c_newFoo >>= newForeignPtr p_freeFoo)

读和写基本上只是一些矫顽力。

代码语言:javascript
复制
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

代码语言:javascript
复制
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

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28768904

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档