我正在嘗試創建一個包含可變向量的記錄,然后列印該記錄。
import qualified Data.Vector.Mutable as VM
data Foo s = Foo {bar :: VM.STVector s Int}
我有一個可以創建實體的函式,
mkFoo :: PrimMonad m => m (Foo (PrimState m))
mkFoo = do
b <- VM.generate 5 (const 3)
return Foo {bar = b}
但是,當我嘗試創建一個列印記錄的函式時,它會出錯。
printFoo :: PrimMonad m => m (Foo (PrimState m)) -> IO ()
printFoo f = do
let b = bar f
printf "%d\n" (VM.read b 0)
它給出了錯誤,
* 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 ()
獲取可變資料的訣竅是什么?
uj5u.com熱心網友回復:
首先,我建議IO在這一點上堅持下去,然后再看,ST因為PrimMonad它們更復雜。對于您的程式,看起來像這樣:
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)
現在你得到的第一個錯誤是:
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
| ^
此訊息告訴您該函式bar需要一個 type 的值Foo作為輸入,但您給它一個 type 的值IO Foo。有多種方法可以解決此問題。由于這個函式在其中運行,IO我們可以首先像這樣解開引數:
printFoo :: IO Foo -> IO ()
printFoo f = do
f' <- f
let b = bar f'
printf "%d\n" (VM.read b 0)
但是,我會說這不是很地道。相反,要求函式的輸入已經被解包會更有用,如下所示:
printFoo :: Foo -> IO ()
printFoo f = do
let b = bar f
printf "%d\n" (VM.read b 0)
現在您仍然會收到另一條錯誤訊息(9.0.1 之前的 GHC 版本將顯示可讀性較差的錯誤):
/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)
| ^
這個錯誤是模糊的,因為該VM.read函式可以以多種不同的方式使用,并且不清楚您打算采用哪種方式。如果您撰寫顯式型別簽名VM.read :: VM.IOVector a -> Int -> IO a,如下所示:
printFoo :: Foo -> IO ()
printFoo f = do
let b = bar f
printf "%d\n" ((VM.read :: VM.IOVector a -> Int -> IO a) b 0)
(您也可以使用TypeApplications擴展名并撰寫VM.read @IO b 0)
然后錯誤訊息變得更加清晰:
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
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/427311.html
標籤:哈斯克尔
