幾天來,我一直在努力構建一個data具有可變值欄位的結構Data.Vector.Mutable
我確認Data.Vector.Mutable自己的行為符合我的預期;但是,一旦將其包含在結構中,就會以某種方式停止違背我的期望。
下面是一個簡單的演示代碼newVal,getVal并setVal針對結構的可變欄位值。
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的基本行為,我嘗試創建、讀取、(重新)寫入和(重新)讀取欄位的可變值;然而,它并沒有像預期的那樣作業。
代碼有什么問題?請指教。
uj5u.com熱心網友回復:
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
這將觸發一堆型別錯誤,因為 eggetVal不再傳遞 IO 操作,而是傳遞了 IO 操作的結果。不過,這正是我們想要的,因此我們需要相應地修復型別。
從這里洗掉IO:
data A a = A
{ ioVal :: M.MVector (PrimState IO) a
}
實際上,我們不想存盤生成向量的程序,我們想存盤向量。
Consequently, we need to remove <- in favor of let in a few other points.
getVal :: A a -> IO a
getVal = \aA -> do
let val = ioVal aA -- no IO action to run here
M.read val 0
Also, newIO must return an IO value.
newIO :: a -> IO (A a)
newIO = \a -> fmap A (newVal a)
I think you can figure out the rest now.
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/427263.html
標籤:哈斯克尔
