首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Data.Vector.Mutable字段无法正确写入/设置?:Haskell

Data.Vector.Mutable字段无法正确写入/设置?:Haskell
EN

Stack Overflow用户
提问于 2022-02-10 22:53:05
回答 1查看 92关注 0票数 3

几天来,我一直在努力构建一个具有可变值的data结构( Data.Vector.Mutable )。

我确认了Data.Vector.Mutable本身的行为与我预期的一样;然而,一旦它包含到结构中,就会以某种方式停止与我的预期相反的工作。

下面是一个演示代码,它简单地以结构的可变字段值为目标,具有newValgetValsetVal

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

代码语言:javascript
复制
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的基本行为,我尝试创建、读取、(Re)写和(Re)读取字段的可变值;但是,它不像应该执行的那样工作。

密码怎么了?请给我建议。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-10 23:15:03

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

代码语言:javascript
复制
main :: IO ()
main = do
  let ioA = newIO (5 :: Int)
  (getVal ioA) >>= print -- 5
  setVal 10 ioA
  (getVal ioA) >>= print -- 5 ?? expected 10

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

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

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

代码语言:javascript
复制
data A a = A
  { ioVal :: M.MVector (PrimState IO) a
  }

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

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

代码语言:javascript
复制
getVal :: A a -> IO a
getVal = \aA -> do
  let val = ioVal aA  -- no IO action to run here
  M.read val 0

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

代码语言:javascript
复制
newIO :: a -> IO (A a)
newIO = \a -> fmap A (newVal a)

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

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

https://stackoverflow.com/questions/71073253

复制
相关文章

相似问题

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