簡體   English   中英

記錄中的 Haskell 可變向量

[英]Haskell mutable vector in record

我正在嘗試創建一個包含可變向量的記錄,然后打印該記錄。

import qualified Data.Vector.Mutable as VM

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

我有一個創建實例的 function ok,

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

但是,當我嘗試創建打印記錄的 function 時,它會出錯。

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 ,稍后再看STPrimMonad ,因為它們更復雜。 對於您的程序,看起來像這樣:

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
   |               ^

此消息告訴您 function bar需要一個Foo類型的值作為輸入,但您給它一個IO Foo類型的值。 有多種方法可以解決此問題。 由於這個 function 在IO中運行,我們可以首先像這樣解開參數:

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

但是,我會說這不是很地道。 相反,要求 function 的輸入已經解包會更有用,如下所示:

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 function 可以以多種不同的方式使用,並且不清楚您想要哪種方式。 如果你寫顯式類型簽名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 function 的參數。 在這種情況下,修復只是在打印之前解開該值:

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

如果您想在另一個 function 中使用它,那么您可以類似地執行此操作,例如在主 function 中:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM