Data.Vector.Unboxed.Mutable 'Vector' を変更する再帰関数を作成しようとしていますが、問題はどのモナド コードにも当てはまると思います。
不自然な例として:
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」が必要です。
v が再帰で渡されないように、add1 を変更して (そして f を同じままにして)、一歩近づくことができます。
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' <- y) を実行する必要はありません (特に add1 が再帰的である場合)。y' (y の「非モナディック」バージョン) がスコープ内にあることを本当に望んでいます。これを行う方法はありますか?
(何らかの方法でモナドをひどく誤用している場合はお詫びします)