简体   繁体   English

记录中的 Haskell 可变向量

[英]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,我有一个创建实例的 function 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.但是,当我尝试创建打印记录的 function 时,它会出错。

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.首先,我建议此时坚持使用IO ,稍后再看STPrimMonad ,因为它们更复杂。 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 .此消息告诉您 function bar需要一个Foo类型的值作为输入,但您给它一个IO Foo类型的值。 There are multiple ways to fix this.有多种方法可以解决此问题。 Since this function runs in IO we can first unwrap the argument like this:由于这个 function 在IO中运行,我们可以首先像这样解开参数:

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:相反,要求 function 的输入已经解包会更有用,如下所示:

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):现在您仍然会收到另一条错误消息(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)
  |                          ^

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.这个错误是模糊的,因为VM.read function 可以以多种不同的方式使用,并且不清楚您想要哪种方式。 If you write the explicit type signature VM.read:: VM.IOVector a -> Int -> IO a ,like this:如果你写显式类型签名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)

(You could also use the TypeApplications extension and write VM.read @IO b 0 ) (您也可以使用TypeApplications扩展并编写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.这告诉您不能使用IO Int类型的值作为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 .如果您天真地尝试调用printFoo mkFoo ,那么您将再次收到一个错误,即无法将预期类型Foo与实际类型IO Foo匹配。 Again the solution is to unwrap, in GHCi you can do that like this:同样,解决方案是展开,在 GHCi 中,您可以这样做:

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:如果您想在另一个 function 中使用它,那么您可以类似地执行此操作,例如在主 function 中:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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