简体   繁体   中英

Haskell mutable vector in record

I'm trying to create a record that contains a mutable vector and then print the record.

import qualified Data.Vector.Mutable as VM

data Foo s = Foo {bar :: VM.STVector s Int}

I have a function that creates an instance ok,

mkFoo :: PrimMonad m => m (Foo (PrimState m))
mkFoo = do
  b <- VM.generate 5 (const 3)
  return Foo {bar = b}

When I try to create a function that prints the record, though, it's giving errors.

printFoo :: PrimMonad m => m (Foo (PrimState m)) -> IO ()
printFoo f = do
  let b = bar f
  printf "%d\n" (VM.read b 0)

It's giving the error,

 * Couldn't match type `m' with `Foo'
      `m' is a rigid type variable bound by
        the type signature for:
          printFoo :: forall (m :: * -> *).
                      PrimMonad m =>
                      m (Foo (PrimState m)) -> IO ()

What's the trick to being able to get at the mutable data?

First of all, I would recommend sticking to IO at this point and look at ST and PrimMonad later as they are more complicated. For your program that would look like this:

import qualified Data.Vector.Mutable as VM
import Text.Printf

data Foo = Foo { bar :: VM.IOVector Int }

mkFoo :: IO Foo
mkFoo = do
  b <- VM.generate 5 (const 3)
  return Foo {bar = b}

printFoo :: IO Foo -> IO ()
printFoo f = do
  let b = bar f
  printf "%d\n" (VM.read b 0)

Now the first error you get is:

M.hs:13:15: error:
    • Couldn't match expected type ‘Foo’ with actual type ‘IO Foo’
    • In the first argument of ‘bar’, namely ‘f’
      In the expression: bar f
      In an equation for ‘b’: b = bar f
   |
13 |   let b = bar f
   |               ^

This message tells you that the function bar expects a value of type Foo as input, but you give it a value of type IO Foo . There are multiple ways to fix this. Since this function runs in IO we can first unwrap the argument like this:

printFoo :: IO Foo -> IO ()
printFoo f = do
  f' <- f
  let b = bar f'
  printf "%d\n" (VM.read b 0)

However, I would say this is not very idiomatic. Instead it is more useful to require that the input to your function is already unwrapped, like this:

printFoo :: Foo -> IO ()
printFoo f = do
  let b = bar f
  printf "%d\n" (VM.read b 0)

Now you will still get another error message (GHC versions before 9.0.1 will show a less readable error):

/tmp/M.hs:9:26: error:
    • Couldn't match type ‘Control.Monad.Primitive.PrimState m0’
                     with ‘GHC.Prim.RealWorld’
      Expected: VM.MVector (Control.Monad.Primitive.PrimState m0) Int
        Actual: VM.IOVector Int
      The type variable ‘m0’ is ambiguous
    • In the first argument of ‘VM.read’, namely ‘b’
      In the second argument of ‘printf’, namely ‘(VM.read b 0)’
      In a stmt of a 'do' block: printf "%d\n" (VM.read b 0)
  |
9 |   printf "%d\n" (VM.read b 0)
  |                          ^

This error is vague, because the VM.read function can be used in many different ways and it is not clear which way you intend. If you write the explicit type signature VM.read:: VM.IOVector a -> Int -> IO a ,like this:

printFoo :: Foo -> IO ()
printFoo f = do
  let b = bar f
  printf "%d\n" ((VM.read :: VM.IOVector a -> Int -> IO a) b 0)

(You could also use the TypeApplications extension and write VM.read @IO b 0 )

Then the error message becomes clearer:

M.hs:14:3: error:
    • No instance for (PrintfArg (IO Int))
        arising from a use of ‘printf’
    • In a stmt of a 'do' block:
        printf "%d\n" ((VM.read :: VM.IOVector a -> Int -> IO a) b 0)
      In the expression:
        do let b = bar f
           printf "%d\n" ((VM.read :: VM.IOVector a -> Int -> IO a) b 0)
      In an equation for ‘printFoo’:
          printFoo f
            = do let b = ...
                 printf "%d\n" ((VM.read :: VM.IOVector a -> Int -> IO a) b 0)
   |
14 |   printf "%d\n" ((VM.read :: VM.IOVector a -> Int -> IO a) b 0)
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This tells you that you cannot use a value of type IO Int as an argument to the printf function. In this case the fix is simply to unwrap the value before printing it:

printFoo :: Foo -> IO ()
printFoo f = do
  let b = bar f
  x <- VM.read b 0
  printf "%d\n" x

Now the file compiles without errors, but how do you actually use these functions? If you naively try to call printFoo mkFoo then you will again get an error about not being able to match the expected type Foo with the actual type IO Foo . Again the solution is to unwrap, in GHCi you can do that like this:

ghci> f <- mkFoo
ghci> printFoo f
3

If you want to use this in another function then you can do that similarly, for example in the main function:

main :: IO ()
main = do
  f <- mkFoo
  printFoo f

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM