ええと、説明する最善の方法は、コードを書くことだと思います。
まず第一に、現在作業しているモナドの内部の仕組みを隠したいと思うでしょう。これは型エイリアスで行いますが、もっと強力な方法があります。Real World Haskellのこの章を参照してください。
type PersonManagement = State Int
これは、後で PersonManagement にさらに何かを追加する場合に備えて、ブラック ボックスの抽象化を使用することをお勧めします。
PersonManagement の定義とともに、このモナドを定義するプリミティブ操作を公開する必要があります。あなたの場合、今のところ tick 関数しかありませんが、これはほとんど同じように見えますが、より明確な署名とより示唆に富む名前が付いています。
generatePersonId :: PersonManagement Int
generatePersonId = do
n <- get
put (n+1)
return n
これで、上記のすべてが別のモジュールに存在するはずです。これに加えて、新しい Person の作成など、より複雑な操作を定義できます。
createPerson :: String -> PersonManagement Person
createPerson name = do
id <- generatePersonId
return $ Person id name
ここまでで、PersonManagement が一種の計算、または Person を処理するためのロジックをカプセル化するプロセスであり、PersonManagement Person
そこから person オブジェクトを取得する計算であることに気付いたでしょう。それはとてもいいことですが、作成したばかりの人を実際に取得して、コンソールでデータを出力するなど、何かを行うにはどうすればよいでしょうか。プロセスを実行して結果を返す「run」メソッドが必要です。
runPersonManagement :: PersonManagement a -> a
runPersonManagement m = evalState m startState
runPersonManagement はモナドを実行し、バックグラウンドですべての副作用を実行しながら最終結果を取得します (この場合、Int 状態をチェックします)。これは状態モナドのevalStateを使用し、モナドの内部動作について知っているため、上記のモジュールにも存在する必要があります。startState で識別される固定値から個人 ID を常に開始する必要があると想定しました。
たとえば、2 人の人物を作成してコンソールに出力する場合、プログラムは次のようになります。
work :: PersonManagement (Person, Person)
work = do
john <- createPerson "John"
steve <- createPerson "Steve"
return (john, steve)
main = do
let (john, steve) = runPersonManagement work
putStrLn $ show john
putStrLn $ show steve
出力:
Person {id = 0, name = "John"}
Person {id = 1, name = "Steve"}
PersonManagement は本格的なモナドであるため、たとえばControl.Monadの汎用関数を使用することもできます。名前のリストから人のリストを作成したいとしましょう。まあ、それはモナドのドメインで持ち上げられた map 関数です - それはmapMと呼ばれます。
createFromNames :: [String] -> PersonManagement [Person]
createFromNames names = mapM createPerson names
使用法:
runPersonManagement $ createFromNames ["Alice", "Bob", "Mike"] =>
[
Person {id = 0, name = "Alice"},
Person {id = 1, name = "Bob"},
Person {id = 2, name = "Mike"}
]
そして、例は続く可能性があります。
あなたの質問の 1 つに答えるために - PersonManagement モナドで作業するのは、そのモナドによって提供されるサービスが必要な場合のみです - この場合、generatePersonId 関数またはモナドのプリミティブwork
を必要とするcreatePerson
関数が必要です。自己インクリメントカウンターが必要なため、PersonManagement モナド内で実行する必要があります。たとえば、2 人の人物が同じデータを持っているかどうかをチェックする関数がある場合、PersonManagement モナド内で作業する必要はなく、通常の純粋な type の関数であるべきPerson -> Person -> Bool
です。
モナドの扱い方を本当に理解するには、多くの例を見ていく必要があります。Real World Haskellは素晴らしいスタートであり、Learn you a Haskell も同様です。
また、モナドを使用するいくつかのライブラリを調べて、モナドがどのように作成され、人々がどのように使用するかを確認する必要があります。1 つの良い例はパーサーであり、parsecは開始するのに最適な場所です。
また、P. Wadler によるこの論文には、いくつかの非常に優れた例が示されています。もちろん、発見される準備ができているリソースは他にもたくさんあります。