簡単な答え
Data.Random.RVar
a の中で 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 Int
CMR.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
あると言うが必要です。そのインスタンスを探してみましょう。s
m
モナドとはm
?RVar
inを実行したい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
のインスタンスであることを示しています。もちろん、これはモナドにも当てはまります。, , は、ランダム ソースによって生成されたランダム値でなければならないこともわかります。RandomSource
m0 Word32
CMR.MonadRandom
s
m0 Word32
s
inとして何を渡す必要がありrunRVar (uniform 0 100) s
ますか? モナドで乱数を生成するものCMR.MonadRandom m => m Word32
、型の何か。CMR
some などの任意のものを生成する関数は何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
の であるとは限らないためです。MonadRandom
gimmeRandom
(\x -> (\x -> f x))
(これは、内側\x
が in で使用されるものであるラムダ用語、または in が最も内側に定義されたものであり、同じである必要はなく、同じ型でさえない のような一次論理と同じシャドーイングの概念ですf x
。、外側のものとして;または実際には、内部スコープで変数の隠蔽/シャドウイングを使用する他のプログラミング言語で-ここでは型変数のシャドウイングです)。∀x . F(x) → ∀x . G(x)
x
G(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
これにより、トップレベルの型シグネチャからm
inが正確getRandom :: m ...
に選択されるようになります。 CMR.MonadRandom m
これはコンパイルされ、問題は解決されます。インターフェイスData.Random
を使用してコード内からディストリビューションを使用できMonadRandom
ます。uniform
を別のディストリビューションに簡単に置き換えることができます。
要約すると、
- 異なるパッケージの 2 つの異なるモナドを使用しているが、セマンティクスが同じ/重複していることを確認しました
- 独自の内部で使用したいモナドを実行/エスケープする方法を見つけました( with
runRVar
)
- 型クラスの制限とそれらに提供されるインスタンスを調べることにより、エスケープ関数に何を渡すかを見つけました
- 正しいコードを書いた (
runRVar (uniform 0 100) getRandom
)
- どの正確なモナド
getRandom
が使用するかを指定してコンパイルしました。
なぜ選択Word32
可能なインスタンスからいくらか恣意的に選択したのか疑問に思われている場合は、何らかの形でランダム性のソースを提供する必要があり、Word32はData.Random
他のランダムなものを生成するための入力として受け取るものの 1 つです。