簡単な答え
Data.Random.RVara の中で aを使用する方法はControl.Monad.MonadRandom?
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad.Random as CMR
import Data.Random as DR
import Data.Word (Word32)
gimmeRandom :: forall m . CMR.MonadRandom m => m Int
gimmeRandom = do
r <- runRVar (uniform 0 100) (getRandom :: m Word32)
return r
説明
事実上、同様のセマンティクスを持つ正式に異なるMonad内で Monad を実行したいとします。
Data.Random.MonadRandomとControl.Monad.Randomは異なる場所で独立して定義されているため形式的にinstance DR.MonadRandom m => CMR.MonadRandom m異なり、どれも他のインスタンスではありません (存在しないか、その逆です)。
- モナドはどちらも何らかの乱数源から乱数を提供するため、似たようなセマンティクスを持っています。
Control.Monad.Randomインターフェイスにコードがあるとします。
import Control.Monad.Random as CMR
gimmeRandom :: CMR.MonadRandom m => m Int
gimmeRandom = do
r <- getRandomR (0, 100)
return r
これを次のようevalRand gimmeRandom StdGenに実行すると、 が得られますInt。
の代わりにgetRandomR、 が提供する多くの利用可能なディストリビューションの 1 つを使用する必要がありますData.Random。
この例では、 で置き換えようとgetRandomR (0, 100)しuniform 0 100 :: RVar Intます。私たちの環境Intでそれをどのように活用するのでしょうか?RVar IntCMR.MonadRandom
RVarセマンティックが示唆するように、おそらく乱数ソースを提供する必要があるモナドを実行したい。CMRのevalRandのようなモナドエスケープ関数を探しています。これらのエスケープ関数には type がありm a -> someStuffNeededToRunTheMonad -> aます。
RVar に関するドキュメントには、次の例があります。
-- In a monad, using a RandomSource:
runRVar (uniform 1 100) DevRandom :: IO Int
確認しましょうrunRVar:
runRVar :: RandomSource m s => RVar a -> s -> m a
はい、これは一種のエスケープ関数です。RVar乱数の と ソースを指定すると、RVar独自の モナド 内のランダムな結果を返しますm。ただし、これには、モナド の乱数源でinstance RandomSource m sあると言うが必要です。そのインスタンスを探してみましょう。sm
モナドとはm?RVarinを実行したいgimmeRandomので、モナドはCMR.MonadRandom m => m( を実装するすべてのモナドCMR.MonadRandom) です。乱数源とは何sですか? まだ手がかりはありません。インスタンスが存在するドキュメントを見てみましょう。RandomSource
RandomSource IO DevRandom
...
Monad m0 => RandomSource m0 (m0 Word32)
Monad m0 => RandomSource m0 (m0 Word64)
...
あはは!これは、任意のモナドが、このモナドからの値(例: )を伴うm0のインスタンスであることを示しています。もちろん、これはモナドにも当てはまります。, , は、ランダム ソースによって生成されたランダム値でなければならないこともわかります。RandomSourcem0 Word32CMR.MonadRandomsm0 Word32
sinとして何を渡す必要がありrunRVar (uniform 0 100) sますか? モナドで乱数を生成するものCMR.MonadRandom m => m Word32、型の何か。CMRsome などの任意のものを生成する関数は何Word32ですか? getRandom . したがって、基本的には次のように記述します。
gimmeRandom :: CMR.MonadRandom m => m Int
gimmeRandom = do
r <- runRVar (uniform 0 100) getRandom
return r
うーん、それはコンパイルされません:
Could not deduce (RandomSource m (m0 a0))
arising from a use of `runRVar'
from the context (CMR.MonadRandom m)
bound by the type signature for
gimmeRandom :: CMR.MonadRandom m => m Int
RandomSource m (m0 a0)? これは奇妙です。mとは、コンパイラによって異なるモナドm0として認識されるようです。のように、それらを同じにしたいのです。RandomSource m0 (m0 Word64)
その場所に完全な署名を入れましょう。
r <- runRVar (uniform 0 100) (getRandom :: CMR.MonadRandom m => m Word32)
それでも同じエラーです。これはm、その型シグネチャの は実際には を実装する任意のモナドであり、型シグネチャCMR.MonadRandomの であるとは限らないためです。MonadRandomgimmeRandom
(\x -> (\x -> f x))(これは、内側\xが in で使用されるものであるラムダ用語、または in が最も内側に定義されたものであり、同じである必要はなく、同じ型でさえない のような一次論理と同じシャドーイングの概念ですf x。、外側のものとして;または実際には、内部スコープで変数の隠蔽/シャドウイングを使用する他のプログラミング言語で-ここでは型変数のシャドウイングです)。∀x . F(x) → ∀x . G(x)xG(x)∀x
getRandomしたがって、私たちがしなければならない唯一のことは、呼び出しで、それが anyMonadRandomではなくMonadRandom m、gimmeRandom型シグネチャにあるものであることをコンパイラに伝えることです。
ScopedTypeVariables拡張機能を使用してそれを行うことができます:
{-# LANGUAGE ScopedTypeVariables #-}
[...]
gimmeRandom :: forall m . CMR.MonadRandom m => m Int
gimmeRandom = do
r <- runRVar (uniform 0 100) (getRandom :: m Word32)
return r
これにより、トップレベルの型シグネチャからminが正確getRandom :: m ...に選択されるようになります。 CMR.MonadRandom m
これはコンパイルされ、問題は解決されます。インターフェイスData.Randomを使用してコード内からディストリビューションを使用できMonadRandomます。uniformを別のディストリビューションに簡単に置き換えることができます。
要約すると、
- 異なるパッケージの 2 つの異なるモナドを使用しているが、セマンティクスが同じ/重複していることを確認しました
- 独自の内部で使用したいモナドを実行/エスケープする方法を見つけました( with
runRVar)
- 型クラスの制限とそれらに提供されるインスタンスを調べることにより、エスケープ関数に何を渡すかを見つけました
- 正しいコードを書いた (
runRVar (uniform 0 100) getRandom)
- どの正確なモナド
getRandomが使用するかを指定してコンパイルしました。
なぜ選択Word32可能なインスタンスからいくらか恣意的に選択したのか疑問に思われている場合は、何らかの形でランダム性のソースを提供する必要があり、Word32はData.Random他のランダムなものを生成するための入力として受け取るものの 1 つです。