Reputation: 463
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?
Upvotes: 1
Views: 329
Reputation: 10645
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
Upvotes: 4