“do”表示法的新范围

时间:2012-07-25 00:59:27

标签: haskell monads

我正在尝试编写一个改变Data.Vector.Unboxed.Mutable'Vector'的递归函数,尽管这个问题适用于任何monadic代码,我想。

作为一个人为的例子:

import Data.Vector.Unboxed as U
import Data.Vector.Unboxed.Mutable as M
import Control.Monad
import Control.Monad.ST
import Control.Monad.Primitive

f :: U.Vector Int -> U.Vector Int
f x = runST $ do
        y <- U.thaw x
        add1 y 0
        U.freeze y

add1 :: (PrimMonad m) => MVector (PrimState m) Int -> Int -> m()
add1 v i | i == M.length v = return ()
add1 v i = do
     c <- M.unsafeRead v i
     M.unsafeWrite v i (c + 1)
     add1 v (i+1)

但是,v在每次递归调用中都不会更改。我希望能够删除v作为函数的参数并将'add1'内联到f中,但我需要'y'在范围内。

我可以更近一步是改变add1(并保持f相同),以便v不会在递归中传递:

add1 :: (PrimMonad m) => MVector (PrimState m) Int -> m()
add1 v = do add1_ 0
    where len = M.length v
          add1_ i | i == len = do return ()
          add1_ i = do
                x <- M.unsafeRead v i
                M.unsafeWrite v i (x + 1)
                add1_ (i+1)

我真正想要的是完全内联add1。这是一个尚未完全编译的解决方案:

f x = let len = U.length x
          y = U.thaw x
          add1 i | i == len = return ()
          add1 i = do
             y' <- y
             c <- M.unsafeRead y' i
             M.unsafeWrite y' i (c+1)
             add1 (i+1)
      in runST $ do
            add1 0
            y' <- y
            U.freeze y'

GHC错误:

couldn't match type 'm0' with 'ST s'
couldn't match type 's' with 'PrimState m0'

除了错误之外,这还不是最优的:我不想在每个do语句中执行(y'&lt; -y)(特别是当add1是递归的时候)。我真的很喜欢y'(y的'非monadic'版本)只是在范围内。有没有办法做到这一点?

(如果我以某种方式可怕地滥用monad,我道歉)

1 个答案:

答案 0 :(得分:5)

这个怎么样?

f :: U.Vector Int -> U.Vector Int
f x = runST $ do
    y <- U.thaw x
    let add1 i | i == length x = return ()
               | otherwise     = do
            c <- M.unsafeRead y i
            M.unsafeWrite y i (c+1)
            add1 (i+1)
    add1 0
    U.freeze y