Haskell 记录中的可变向量
Haskell mutable vector in record
我正在尝试创建一个包含可变向量的记录,然后打印该记录。
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 ()
获取可变数据的诀窍是什么?
首先,我建议此时坚持使用 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
需要一个 Foo
类型的值作为输入,但您给它一个 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)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
这告诉您不能使用 IO Int
类型的值作为 printf
函数的参数。在这种情况下,修复只是在打印之前解包值:
printFoo :: Foo -> IO ()
printFoo f = do
let b = bar f
x <- VM.read b 0
printf "%d\n" x
现在文件编译没有错误,但是你如何实际使用这些函数呢?如果您天真地尝试调用 printFoo mkFoo
,那么您将再次收到有关无法将预期类型 Foo
与实际类型 IO Foo
匹配的错误。同样,解决方案是解包,在 GHCi 中你可以这样做:
ghci> f <- mkFoo
ghci> printFoo f
3
如果你想在另一个函数中使用它,那么你可以类似地做,例如在主函数中:
main :: IO ()
main = do
f <- mkFoo
printFoo f
我正在尝试创建一个包含可变向量的记录,然后打印该记录。
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 ()
获取可变数据的诀窍是什么?
首先,我建议此时坚持使用 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
需要一个 Foo
类型的值作为输入,但您给它一个 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)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
这告诉您不能使用 IO Int
类型的值作为 printf
函数的参数。在这种情况下,修复只是在打印之前解包值:
printFoo :: Foo -> IO ()
printFoo f = do
let b = bar f
x <- VM.read b 0
printf "%d\n" x
现在文件编译没有错误,但是你如何实际使用这些函数呢?如果您天真地尝试调用 printFoo mkFoo
,那么您将再次收到有关无法将预期类型 Foo
与实际类型 IO Foo
匹配的错误。同样,解决方案是解包,在 GHCi 中你可以这样做:
ghci> f <- mkFoo
ghci> printFoo f
3
如果你想在另一个函数中使用它,那么你可以类似地做,例如在主函数中:
main :: IO ()
main = do
f <- mkFoo
printFoo f