Data.Vector.Mutable字段无法正确写入/设置?:哈斯克尔



我几天来一直在努力构建一个具有可变值为Data.Vector.Mutable的字段的data结构

我确认Data.Vector.Mutable本身的行为符合我的预期;然而,一旦它被包含在一个结构中,不知何故,它就停止了与我的预期相反的工作。

下面是一个演示代码,它只包含针对结构的可变字段值的newValgetValsetVal

newIO是类型为newIO :: a -> A a的数据结构的构造函数。

module Main where
import Control.Monad.Primitive (PrimMonad (PrimState))
import qualified Data.Vector.Mutable as M 
------------------------------------ 
data A a = A
{ ioVal :: IO (M.MVector (PrimState IO) a)
}
newIO :: a -> A a
newIO = a -> A (newVal a)
------------------------------
newVal :: a -> IO (M.MVector (PrimState IO) a)
newVal = a -> do
val <- M.new 1
M.write val 0 a
return val
getVal :: A a -> IO a
getVal = aA -> do
val <- ioVal aA
M.read val 0
setVal :: a -> A a -> IO ()
setVal = a -> aA -> do
val <- ioVal aA
M.write val 0 a
------------------------------
main :: IO ()
main = do
let ioA = newIO (5 :: Int)
(getVal ioA) >>= print -- 5
setVal 10 ioA
(getVal ioA) >>= print -- 5 ?? expected 10

因此,在这里,为了确认结构的set/get的基本行为,我尝试创建、读取、(重新(写入和(重新(读取字段的可变值;但是,它并不像集合应该执行的那样工作。

代码出了什么问题?请告知。

Haskell的一个主要特性是引用透明性:我们总是可以用它们的定义替换已定义的实体。现在考虑发布的代码:

main :: IO ()
main = do
let ioA = newIO (5 :: Int)
(getVal ioA) >>= print -- 5
setVal 10 ioA
(getVal ioA) >>= print -- 5 ?? expected 10

这定义了ioA,所以我们可以用它自己的定义来代替它。我们得到:

main :: IO ()
main = do
(getVal (newIO (5 :: Int))) >>= print -- 5
setVal 10 (newIO (5 :: Int))
(getVal (newIO (5 :: Int))) >>= print -- 5 ?? expected 10

现在我们可以看到问题了:我们创建了三个独立的向量。问题是let ioA = ...定义了一个IO操作(大致上是一个命令式过程(,我们以后可以多次调用它。但我们不希望这样:我们希望newIO (5 :: Int)只执行一次。

为此,我们必须避免使用let并使用一元绑定(<-,在do块中(。

main :: IO ()
main = do
ioA <- newIO (5 :: Int)   -- run the action, just once
(getVal ioA) >>= print
setVal 10 ioA
(getVal ioA) >>= print

这将触发一系列类型错误,因为例如getVal不再传递IO操作,而是传递IO操作的结果。不过,这正是我们想要的,所以我们需要相应地修复类型。

从此处删除IO开始:

data A a = A
{ ioVal :: M.MVector (PrimState IO) a
}

事实上,我们不想存储生成向量的过程,我们想存储向量。

因此,在其他几点上,我们需要删除<-,以支持let

getVal :: A a -> IO a
getVal = aA -> do
let val = ioVal aA  -- no IO action to run here
M.read val 0

此外,newIO必须返回一个IO值。

newIO :: a -> IO (A a)
newIO = a -> fmap A (newVal a)

我想你现在可以弄清楚剩下的了。

最新更新