ノードと一意のキーの IntMap を使用してグラフ構造を作成したいと考えています。このトピックはこことここでよく取り上げられています. 基本的に state -> (val,state) の関数を newtype でラップすることで状態モナドがどのように機能するかを理解しているので、そのモナドインスタンスを作成できます。私はそのトピックについてかなり読んだことがあります。プログラムの実行中に一意の(または単なる増分)値を取得する方法について、まだ頭を悩ませているようです。連続した ID の実行を取得するのは簡単ですが、モナドから抜け出すために "runState" を実行すると、現在の ID を追跡しなければならなかった場所に戻ったように思えます。モナドに閉じ込められたような気がします。私が検討したもう 1 つのオプションは、IntMap 全体と現在の「次の」ID を状態として保持することでしたが、それは非常に「必須」で極端に思えます。これ質問は非常に似ていますが、多くの回答が得られませんでした(または、明らかな何かが欠けているだけかもしれません)。プログラムの実行中に状態モナドを利用して一意の ID を取得する慣用的な方法は何ですか? ありがとう。
1 に答える
IO
モナドを-化することを想像してみましょうState
。それはどのように見えるでしょうか?私たちの純粋なState
モナドは、周りの単なるニュータイプです:
s -> (a, s)
まあ、IO
バージョンは最終的な値を返す前に少し副作用をするかもしれません、それは次のようになります:
s -> IO (a, s)
そのパターンは非常に一般的で、名前があります。具体的にはStateT
:
newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }
T
モナドトランスフォーマーであるため、名前の最後にaが付いていT
ます。m
「ベースモナド」とStateT s m
「変換された」モナドと呼びます。
StateT s m
Monad
の場合のみm
ですMonad
:
instance (Monad m) => Monad (StateT s m) where {- great exercise -}
ただし、それに加えて、すべてのモナド変換子MonadTrans
は次のように定義されたクラスを実装します。
class MonadTrans t where
lift :: (Monad m) => m a -> t m a
instance MonadTrans (StateT s) where {- great exercise -}
の場合、のタイプt
は次のことに特化しています。StateT s
lift
lift :: m a -> StateT s m a
つまり、ベースモナドのアクションを「持ち上げ」て、変換されたモナドのアクションにすることができます。
したがって、特定の問題については、追加の。StateT (IntMap k v) IO
で拡張されるモナドが必要です。次に、このモナドでプログラム全体を記述できます。IO
State
main = flip runStateT (initialState :: IntMap k v) $ do
m <- get -- retrieve the map
lift $ print m -- lift an IO action
(k, v) <- lift readLn
put (insert k v m)
とをまだ使用get
していることに注意してくださいput
。これは、transformers
パッケージが私が説明したすべての概念を実装し、次のように署名を一般化するget
ためput
です。
get :: (Monad m) => StateT s m s
put :: (Monad m) => s -> StateT s m ()
つまり、。内で自動的に機能しますStateT
。 transformers
次に、次のように定義State
します。
type State s = StateT s Identity
つまり、との両方にとを使用できるget
というput
ことState
ですStateT
。
モナド変換子の詳細については、モナド変換子-ステップバイステップを強くお勧めします。