I have struggled for days to compose a data
structure that has a field of mutable value of Data.Vector.Mutable
I confirmed Data.Vector.Mutable
itself behaves as I expected; however once it's included into a structure, somehow stops working against my expectation.
Below is a demo-code that simply has newVal
, getVal
and setVal
targeting the mutable field value of the structure.
newIO
is the constructor of the data structure typed as 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
So, here, to confirm the basic behavior of set/get of the structure, I try to create, read, (re)write, and (re)read the mutable value of the field; however, it does not work as expected as the set should perform.
What's wrong with the code? Please advise.
CodePudding user response:
A main property of Haskell is referential transparency: we can always replace defined entities with their definitions. Now consider the posted code:
main :: IO ()
main = do
let ioA = newIO (5 :: Int)
(getVal ioA) >>= print -- 5
setVal 10 ioA
(getVal ioA) >>= print -- 5 ?? expected 10
This defines ioA
, so we can replace it with its own definition. We get:
main :: IO ()
main = do
(getVal (newIO (5 :: Int))) >>= print -- 5
setVal 10 (newIO (5 :: Int))
(getVal (newIO (5 :: Int))) >>= print -- 5 ?? expected 10
Now we can see the problem: we create three independent vectors. The issue is that let ioA = ...
defines an IO action (roughly, an imperative procedure) that we can call multiple times later on.
But we don't want that: we want newIO (5 :: Int)
to be executed only once.
For that, we must avoid let
and use monadic bind (<-
, in do
blocks).
main :: IO ()
main = do
ioA <- newIO (5 :: Int) -- run the action, just once
(getVal ioA) >>= print
setVal 10 ioA
(getVal ioA) >>= print
This will trigger a bunch of type errors, since e.g. getVal
is no longer passed an IO action, but is passed the result of the IO action. This is what we want, though, so we need to fix the types accordingly.
Start by removing IO
here:
data A a = A
{ ioVal :: M.MVector (PrimState IO) a
}
Indeed, we don't want to store a procedure that makes a vector, we want to store the vector.
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.