Home > Mobile >  Data.Vector.Mutable field can not be written/set properly? : Haskell
Data.Vector.Mutable field can not be written/set properly? : Haskell

Time:02-11

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.

  • Related