7

これをコンパイルしようとすると:

module Main where

import qualified Data.Vector.Unboxed.Mutable as MV
import Control.Monad.ST

myRead mv = runST $ MV.read mv 0

次のエラー メッセージが表示されます。

Could not deduce (t ~ U.MVector s a)
    from the context (U.Unbox a)
      bound by the inferred type of myRead :: U.Unbox a => t -> a
      at src/Main.hs:53:1-32
      `t' is a rigid type variable bound by
          the inferred type of myRead :: U.Unbox a => t -> a
          at src/Main.hs:53:1
    Expected type: U.MVector (PrimState (ST s)) a
      Actual type: t
    In the first argument of `MV.read', namely `mv'
    In the second argument of `($)', namely `MV.read mv 0'
    In the expression: runST $ MV.read mv 0

runST を使用して可変ベクトルからの読み取りをピュアにすることはできますか? もしそうなら、どのように?の型署名が必要だとmyRead思いますが、私が試したすべてのことは、ますます理解できないエラーメッセージにつながりました。

編集:以下のコメントに入れただけのコンテキストを強調しています:ここでのコンテキストは、変更可能なベクトルを受け取り、変更可能なベクトルを一時的なスクラッチスペースとして使用していくつかの計算を行い、浮動小数点値を返す必要があるということです。可変ベクトルへの変更は気にしないので、その「状態の変化」を無視して、その中から値の 1 つを単に返す方法があるかどうか疑問に思っていました。

4

3 に答える 3

3

mvコンパイラは、デフォルトで左側の引数を特定の型として認識しますが、右側には多相型の何かが必要です。型シグネチャは物事を修正できます。

{-#LANGUAGE Rank2Types#-}
module Main where

import qualified Data.Vector.Unboxed.Mutable as MV
import Control.Monad.ST

myRead :: MV.Unbox a => (forall s . MV.MVector s a) ->  a
myRead mv =  runST $ MV.read mv 0

私が理解すれば、あなたの関数のシグネチャは次のようになります。

-- myBadRead :: forall s a . MV.Unbox a => MV.MVector s a -> a 
-- myBadRead mv =  runST $ MV.read mv 0

しかし、ここでは、LHSに書き込むときに修正されるため、独立した作業runST :: (forall s. ST s a) -> aはありません。ssmv

mv編集: ただし、Joachim B と Daniel F. が強調しているように、上記の定義は首尾一貫していますが、それに与えるベクトルを作成できないため、実際には役に立ちません。大雑把に言えば、 を生成するどの方法でも、コンパイラによってフードの下でmv既に決定が割り当てられています。s標準的な方法の 1 つは、そのような関数を「純粋な」ベクトルでData.Vector.Unboxed動作させ、右側で、.Mutableモジュールから操作を適用する前に解凍することです。

import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Unboxed.Mutable as MV
import Control.Monad.ST

myRead :: MV.Unbox a => UV.Vector a ->  a
myRead v =  runST $ do mv <- UV.unsafeThaw v
                       MV.read mv 0

もちろん、この特定の定義はmyRead = (UV.! 0)同様に同等であり、このようなものは理にかなっrunSTています。myRead

mhead :: MV.Unbox a => MV.MVector s a -> ST s a
mhead mv0 =  MV.read mv0 0

mrx  = runST $ do mv <- UV.unsafeThaw $ UV.enumFromStepN 0 1 20
                             -- ^^^ or however the mv is generated.
                  x <- MV.unsafeRead mv 17 -- arbitrary 'scratch pad'
                  MV.unsafeWrite mv 17 (2*x)  -- computations
                  mhead mv
                  -- ^^^ we return just the first element, after all the mutation

ここでは、閉じたり閉じmyReadたりmheadするのではなくrunST、多態性を維持し、可変ベクトルが表示されるs同じブロック内で使用できます。したがって、コンパイラは、 do ブロック全体に使用している「秘密」を使用して、 に適用し た結果を解釈できます。STmvsmheadmvmhead

于 2012-08-03T19:16:30.840 に答える
3

applicative による回答は、コードをコンパイルする方法を示しています。しかし、コードは使用できません。ポイントはrunST、そこに存在する型変数がバインドされているため、命令型計算がコードをエスケープできないことです。

どこかで作成した変更可能な配列はMVector s a固定の s の型を持ちますが、任意myReadの s のベクトルを提供する値を期待します。

その(不可能な)機能が欲しいと思ったのは、以前の問題があったようです。

于 2012-08-03T19:22:29.613 に答える
2

他の答えは良いですが、STについて欠けている基本的なことが1つあると思います。runST を呼び出すたびに、命令型コードが実行される新しい「ST ユニバース」が効果的に作成されます。そのため、配列を作成するために runST を 1 回呼び出し、その配列から値を取得するために runST を個別に呼び出すと、うまくいかない可能性があります。2 つの runST 呼び出しは独自のユニバースを必要としますが、1 つを共有する必要があります。

答えが詳細に説明しているのは、これらのユニークな宇宙が型システムの策略によってどのように作成されるかということです。

于 2012-08-06T01:55:47.357 に答える