タイトルの通りです。
いろいろな使用例を載せたので、頑張って使いこなせるようになりましょう。
私自身ちゃんと理解しているわけではなく、自分の勉強をかねて書いているので間違いなどあったら指摘してくださると助かります。
また、mwc-random以外のパッケージの関数も使用しますが、これらについてはあまり説明せず使用します(Data.Vector.Unboxed, Control.Monad.ST など)。
STモナドを使う例をあまり載せていませんが、IOのときと同じような感じで書けるはずです(たぶん...)。


記事の流れ
1. とりあえず簡単に乱数生成してみる
 (createSystemRandom, uniform, uniformR)
2. 少し頑張って乱数列を生成してみる
 (uniformVector)
3. 乱数を生成する関数を引数に取る関数を使う
 (withSystemRandom, asGenIO)
4. 毎回同じ値を生成する
 (toSeed, restore, initialize)
5. 乱数の種(seed)とベクトル
 (toSeed, fromSeed)
6. 純粋な関数の中で使うためのインターフェースを作ってみる
 (restore, save)
7. haskell stackでmwc-randomパッケージをインポートする方法


1. とりあえず簡単に乱数生成してみる
(createSystemRandom, uniform, uniformR使用方法)
IOモナド内で乱数を生成して、printしてみます。
-- サンプル1-1
import System.Random.MWC

main :: IO ()
main = do
    gen <- createSystemRandom
    randValue <- uniform gen :: IO Int
    print randValue
これだけです。
(System.Random.MWCをimportするには、パッケージをダウンロードする必要があります。haskell stackを使っている場合は非常に簡単なので、方法を記事の最後に書きました。stackを使っていない場合は、頑張ってください......)


createSystemRandomは擬似乱数生成器(PRNG)を生成します。
createSystemRandom :: IO GenIO
より、これは引数を取りません。
またIOモナド内だけで使える関数です。


uniformは乱数をひとつ生成します(乱数範囲指定不可、型は指定する必要あり)。
uniform :: PrimeMonad m => Gen (PrimeState m) -> m a
より、これはPRNGを引数に取って乱数を返します。
サンプル1-1でのuniformの具体型は
uniform :: Gen (PrimeState IO) -> IO Int
となっています(GenIOは、Gen (PrimeState IO)の別名です)。
生成する乱数の型はサンプル1-1のように指定する必要があります(:: IO Int)。
でないと、 m a の具体型が決まらないのでエラーになります(The type variable 'a' is ambiguous などと言われます)。
もちろん、IntではなくDoubleBoolなどを指定しても構いません。


uniformRは範囲を指定して乱数を生成したいときに使います。
サンプル1-1の7行目を以下のように書き換えてみてください。
randValue <- uniformR (1.0, 10.0) gen :: IO Double
こうすると、1.0 〜 10.0までの範囲のDouble型の乱数が生成されるはずです。
uniformRの型は、
uniformR :: PrimeMonad m => (a, a) -> Gen (PrimeState m) -> m a
より、これは第1引数で乱数の最小値と最大値を指定し、第2引数にPRNGをとって乱数を返します。
上の例での具体型は、
uniformR :: (Double, Double) -> Gen (PrimeState IO) -> IO Double
となっています。

話が戻りますが、createSystemRandomで生成したPRNGは、最初に一度だけ生成すれば使い回すことができます。
こんな感じです。
-- サンプル1-2
import System.Random.MWC

main :: IO ()
main = do
    gen <- createSystemRandom
    print =<< (uniformR (0, 99) gen :: IO Int)
    print =<< (uniformR (0, 99) gen :: IO Int)
    print =<< (uniformR (0, 99) gen :: IO Int)
    print =<< (uniformR (0, 99) gen :: IO Int)
    print =<< (uniformR (0, 99) gen :: IO Int)
同じgenを何度も使用していますが、毎回異なる乱数が生成されるはずです。
PRNGの生成は、乱数生成に比べて "expensive" (参考[2]より)なので、使いまわしたほうがいいようです。


2. 少し頑張って乱数列を生成してみる
(uniformVector)
mwc-randomでは、生成した乱数列はリストではなくVectorとして返されます。
HackageのData.Vectorドキュメント(同じタブで開きます))
例えば以下のように使います。
--サンプル2-1
import System.Random.MWC
import qualified Data.Vector.Unboxed as VU

main :: IO ()
main = do
    gen <- createSystemRandom
    randValues <- uniformVector gen 10 :: IO (VU.Vector Int)
    print randValues
これをghci上で実行すると以下のように、10個の乱数のベクトルが返されます。
42
リストのように見えますが、uniformVectorの型は以下のようになっています。
uniformVector :: (PrimMonad m, Variate a, Vector v a) => (Gen PrimState m) -> Int -> m (v a)
戻り値の型はモナドに包まれたベクトルです。
だから上のコード(サンプル2-1)のrandValueの型はVU.Vector Int となるわけです。
もしリストに変換したいなら、toListという関数を使って変換することができます。
let randValues' = VU.toList randValues
これで、randValues'の型は[Int]となります。



3. 乱数を生成する関数を引数に取る関数を使う
(withSystemRandom, asGenIO)
ここまでは、PRNG(擬似乱数生成器)を生成するのに、毎回createSystemRandomを使ってきました。
これをwithSystemRandomという関数で書き換えてみます。
この関数の型は、
withSystemRandom :: PrimBase m => (Gen (PrimState m) -> m a) -> IO a
となっています。
つまり引数に関数をとって、IOモナドに包まれた乱数を返します。
引数の関数は、PRNGを引数に取って乱数を返す関数です。
例えば、最初に紹介したuniformはここに入るぴったりの関数です。
こんな風に使います。
main :: IO ()
main = do
    randValue <- withSystemRandom (asGenIO uniform) :: IO Int
    print randValue
Haskellらしく、()を使わずに書くとこんな感じになります。
main :: IO ()
main = do
    randValue <- withSystemRandom . asGenIO $ uniform :: IO Int
    print randValue
asGenIOは、Gen (PrimState m)GenIOと指定するための関数です。
これを使わないと、GenIOなのかGenSTなのか決まらないためエラーになり、ambiguous と言われます。

しかし、以下のようにラムダ式を使えば、asGenIOを使わなくても書けます。
main :: IO ()
main = do
    randValue <- withSystemRandom $ \\gen -> uniform gen :: IO Int
    print randValue

さらに、asGenIOとラムダ式を使えば、このような書き方もできます。
main :: IO ()
main = do
    randValue <- withSystemRandom . asGenIO $ \\gen -> uniform gen
    print (randValue :: Int)
どういう場合に型推論ができて、どういう場合に型推論ができないのかどうもよくわかりません。


ではwithSystemRandomを使って、乱数列も生成してみます。
uniformは、引数にPRNGのみを取る関数だからぴったりでしたが、uniformVectorは引数をふたつ取る(PRNGとInt整数)ので、少し工夫が必要です。
こんな感じです(これはHackageのドキュメントにあるサンプルとほとんど一緒です)。
main :: IO ()
main = do
    randValues <- withSystemRandom . asGenIO $ \\gen -> uniformVector gen 10 :: IO (VU.Vector Int)
    print randValues



4. 毎回同じ値を生成する
(initialize, toSeed, restore)
決まった値をもとに乱数の種(seed)を初期化すれば、毎回同じ値を生成することができます。
--サンプル3-1
import System.Random.MWC
import Control.Monad.ST (runST)
import qualified Data.Vector.Unboxed as VU

main :: IO ()
main = do
    print pureRandValue

pureRandValue :: Int
pureRandValue = runST $ do
    gen <- initialize $ VU.fromList [1,3,8,20]
    randValue <- uniform gen
    return (randValue :: Int)
今までは、createSystemRandomでPRNGを生成していましたが、ここではそれをinitializeで生成しています。
initializeの型は、
initialize :: (PrimMonad m, Vector v Word32) => v Word32 -> m (Gen (PrimState m))
となっています。
つまり、与えられたベクトルの値をもとにPRNGを生成するので、毎回同じPRNGが生成されます。
したがって、生成される乱数も毎回同じです。

initializeの引数にベクトルを渡すため、fromListを使って、リストからVectorへ変換しています。
そのあとは、これまでと同じです。

サンプル3-1では、seedを初期化するのがわかりにくいので、以下のように書き換えてみます。
--サンプル3-2
import System.Random.MWC
import Control.Monad.ST (runST)
import qualified Data.Vector.Unboxed as VU

main :: IO ()
main = do
    print pureRandValue'

pureRandValue' :: Int
pureRandValue' = runST $ do
    let seed = toSeed . VU.fromList $ [1,3,8,20]
    gen <- restore seed
    randValue <- uniform gen
    return (randValue :: Int)
結果は同じです。
つまりinitializeは、restore . toSeedと同じことをする関数です。

一つずつ見てきます。
toSeedの型は、
toSeed :: Vector v Word32 => v Word32 -> Seed
これは、ベクトルからSeedを生成します。

restoreの型は、
restore :: PrimMonad m => Seed -> m (Gen (PrimState m))
これは、SeedからPRNGを生成します。
つまり、initialize、あるいはrestore . toSeedは、ベクトルからPRNGを生成します。
言葉より図のほうがわかりやすいので、図を作ってみました。
mwc-random-vec-seed-gen_Fotor
Vector型、Seed型、Gen型の変換の模式図です。
Gen型は常にモナドに包まれていますが、省略しています。
図でみるとinitialize = restore . toSeedも一目瞭然ですね。
まだ説明していない関数も書いてありますが、後ほど説明します。

Hackageのドキュメント(参考[2])のinitializeの説明のところにこんな例があります。
gen' <-  initialize . fromSeed =<< save
そしてこの時、gen' == genだと書いてありました。
つまり、genを一旦ベクトルまで戻してからまたgen'を生成(gen --> seed --> vector --> seed --> gen')しても、同じだということです。
実際に以下のコードで試してみました。
import System.Random.MWC
import qualified Data.Vector.Unboxed as VU

main :: IO ()
main = do
    gen <- initialize $ VU.fromList [1]
    gen' <- initialize . fromSeed =<< save gen
    rand <- uniform gen :: IO Int
    rand' <- uniform gen' :: IO Int
    print $ rand == rand'
6行目でまずgenを生成します。
7行目で、先ほどの例のように、gen'を生成します。
10行目で、randrand'が等しいか出力します。
確かに、Trueが出力されました。
ちなみにどうでもいいですがモナドを使う練習として、8 ~ 10行目は以下のように書き換えることができます。
print =<< (==) <$> (uniform gen :: IO Int) <*> (uniform gen' :: IO Int)
ここでは、バインド(=<<)のようなことを2引数関数(==)でやっています。
そしてそれをprintへバインドしています。
まだ全然モナドを使いこなせませんが、覚えておこうと思います。



5. 乱数の種(seed)とベクトル
(toSeed, fromSeed)
上の図を見るとわかりますが、toSeedには逆関数fromSeedがあります。
型を比べるとよくわかります。
toSeed :: Vector v Word32 => v Word32 -> Seed
fromSeed :: Seed -> Vector Word32
ちょうど引数と戻り値の型が逆になっているのがわかります。
つまり、toSeedがベクトルからseedを生成するのに対し、fromSeedはseedをもとのベクトルへ戻します。
乱数を生成するためにわざわざベクトルからseedへ変換したのに、なぜまた元に戻す関数が存在するのか、私にはよくわかりません。


6. 純粋な関数の中で使うためのインターフェースを作ってみる
(restore, save)
だんだん面倒になってきましたが、とりあえず書きます。
restoresaveも逆関数の関係です。
型を見比べてください。
restore :: PrimMonad m => Seed -> m (Gen (PrimState m))
save :: PrimMonad m => Gen (PrimState m) -> m Seed
restoreseedからPRNGを生成する関数、saveはPRNGからもとのseedを取り出す関数です。
saveは、PRNGの状態を保存したい時に使います。
例として、参考[1]にあった例をパクらせていただきます、すみません(少し単純にしていますが)。
import Control.Monad.ST (runST)
import System.Random.MWC
import qualified Data.Vector.Unboxed as VU

main :: IO ()
main = do
    let (rand, seed) = randomMWC $ toSeed . VU.fromList $ [1] :: (Int, Seed)
    let (rand', _) = randomMWC seed :: (Int, Seed)
    print rand
    print rand'

randomMWC :: Variate t => Seed -> (t, Seed)
randomMWC seed = runST $ do
    gen <- restore seed
    randValue <- uniform gen
    seed' <- save gen
    return (randValue, seed')
randomMWCの型をみてください。
純粋な値(Seed)を受け取って、純粋な値((t, Seed))を返しています。
つまり、内部ではSTモナドを使用していますが、この関数は純粋な関数の中で呼び出すことができます。
同様にして、範囲指定する場合の関数も作ってみます。
randomRMWC :: Variate t => Seed -> (t, t) -> (t, Seed)
randomRMWC seed range = runST $ do
    gen <- restore seed
    randValue <- uniformR range gen
    seed' <- save gen
    return (randValue, seed')
uniformのところをuniformRに変えて、あと引数に範囲を指定するタプルを追加しただけです。
この関数を使えば、純粋な関数内で範囲指定のある乱数生成ができます。

さらに、これらを使って、乱数列を生成する関数も作ってみます。
randomsMWC :: Variate a => Seed -> [a]
randomsMWC seed = rand : randomsMWC seed'
    where (rand, seed') = randomMWC seed

randomRsMWC :: Variate a => (a, a) -> Seed -> [a]
randomRsMWC range seed = rand : randomRsMWC range seed'
    where (rand, seed') = randomRMWC range seed
randomsMWCは、範囲指定なしの乱数の無限リストを返します。
randomRsMWCは、範囲指定ありの乱数の無限リストを返します。
使い方は、こんな感じです。
import Control.Monad.ST (runST)
import System.Random.MWC
import qualified Data.Vector.Unboxed as VU

main :: IO ()
main = do
    let rands = take 5 $ randomsMWC $ toSeed $ VU.fromList [1] :: [Double]
    let randRs = take 5 $ randomRsMWC (0, 9) $ toSeed $ VU.fromList [1] :: [Int]
    print rands
    print randRs
これで、純粋な関数内で乱数を一つ生成することもできるし、乱数列を生成することもできます。
ただし、おそらくrestoresaveは乱数生成に比べてコストが大きいので、乱数を一つ生成するたびにrestoresaveを繰り返すこれらの関数は、実用的ではないかもしれません(System.Randomの関数とどっちが速いか気になります)。



7. haskell stackでmwc-randomパッケージをインポートする方法
Haskell stackを使っている場合の方法を書きます。
まず、
$ stack new MWC-random new-templates
などとして、プロジェクトを作成してください(MWC-randomの部分はご自分の好きな名前でどうぞ)。
$ cd MWC-random
で、MWC-random/へ移動すると、MWC-random.cabalというファイルがあります。
このファイルを開き、build-dependsに"mwc-random"を追記します。
app/Main.hs内でインポートする場合は、executableのbuild-dependsへ追記してください。
src/Lib.hs内でインポートする場合は、libraryのbuild-dependsへ追記してください。
executableに追加する場合はこんな感じ
BlogPaint

たったこれだけです。
これであとは、
$ stack build
するときに、勝手にダウンロードしてくれます。




参考

[1]Haskellの乱数事情(Qiita)(同じタブで開きます)

[2]https://hackage.haskell.org/package/mwc-random-0.13.4.0/docs/System-Random-MWC.html(これがドキュメント、同じタブで開きます)

[3]https://hackage.haskell.org/package/mwc-random(同じタブで開きます)


コメントを書いていただけると有難いです。
特に、間違いなどあったらご指摘いただけると幸いです。
よろしくお願いします。