2016-03-23 11 views
7

実際の例:私が良い気分(「良い状態」)にいる場合、マネージャーが見積もりについて私に尋ねるとき、私は彼に確かな答えを与えますが、それを3回実行することを敢行します、間にいくつかの無料の軽食がなくて、私の気分が変わる(私は「悪い状態」になる)、次の3回彼に近づく。彼はナンセンスで私に邪魔しないように頼む。ここでなぜrunStateシグネチャには状態引数だけがありますか?

は私のいつもの日のログです:

       [ Mood: Good, Patience: 3 ] -- 11:00 am, I'm happy 
ESTIMATE -> "bla bla 6",  [ Mood: Good, Patience: 2 ] 
ESTIMATE -> "bla bla 1",  [ Mood: Good, Patience: 1 ] 
Cookies! -> "",    [ Mood: Good, Patience: 3 again! ] 
ESTIMATE -> "bla bla 7",  [ Mood: Good, Patience: 2 ] 
ESTIMATE -> "bla bla 2",  [ Mood: Good, Patience: 1 ] 
ESTIMATE -> "bla bla 9",  [ Mood: BAD , Patience: -2 ] -- Enough! 
ESTIMATE -> "Need a break!" [ Mood: BAD , Patience: -1 ] 
ESTIMATE -> "Deploynig!", [ Mood: BAD , Patience: 0 ] 
ESTIMATE -> "Lunch time!", [ Mood: Good, Patience: 3 ] -- Ok he needs me.. 
ESTIMATE -> "bla bla 6",  [ Mood: Good, Patience: 2 ] 
... 

今仕事で、私のこのモデルは、Stateモナドを合わせているようです。

newtype State s a = State { runState :: s -> (a, s) } 

しかし、どうすればよいですか?署名には州のための余地があります。私のケースでは(Mood,Patience)であり、入力ではありません(ESTIMATEまたはCookies)。それは私が聞いていなくても答えなければならないようなものです!

私の質問は:ステートフルであるだけでなく、HaskellのStateモナドでの引数計算もどうすればできますか?

答えて

8

ステートフル計算は、入力と状態を取得し、出力と新しい状態を返します。したがって、タイプはinput -> state -> (state, output)になります。

runStateは、入力を既に受けている部分的にステートフルな計算に過ぎません。 (あなたが>>=バインドオペレータまたはdo表記を使用する場合、すなわち)あなたはステートフルな機能を構成すること

も注意してください、あなたはまさにこの操作を行います。あなたは表現としての入力を供給し、バインドの周りだけ状態を渡すために責任があるが。

戻り値を使用せずにgetに電話をかけることはできますが、それは失われます。あなたがそれを利用したい場合は、value <- getを使用し、次のステートフル計算のための明示的な入力としてvalueを提供する必要があります。バインドは、状態を周回するときにのみ作用します。


実践例:関数を考えてみます。

doStuff :: Int -> State Int Int 
doStuff x = do 
    val <- get 
    put $ val+x+1 
    return 0 

doStuffタイプは正確にパターンinput -> state -> (state, output)を持っています。しかし、input部分はx引数で表されます。 xを入力すると、タイプstate -> (state, output)のものが得られます。これはまさにrunStateのものです。

ステートフルアクションの引数は、実際には「入力がない純粋なステートフルな計算」(恐ろしい引用符)を得るために部分的に適用できるので、実際には引数を必要としません。

2

あなたが探していることはStateしかしStateTではないようですが、既存のモナドにステートフルを追加モナド変換子が鳴ります。タイプsの状態を考えると

newtype StateT s m a = StateT (s -> m (a, s)) 

StateT s m aアクションは、実行時に、その結​​果、新たな状態を作り出す mアクションを返します。 StateT sMonadTransのインスタンスである:また

instance MonadTrans (StateT s) where 
    --lift :: Monad m => m a -> StateT s m a 
    lift ma = StateT $ 
    \s -> ma >>= \a -> pure (a, s) 

mMonadある場合、そのようにStateT s mあります。

だから、あなたには、いくつかの文脈の中で、「入力」を取りたい場合(例えば、IO)、あなたはそうするのは自由です。特にIOについては

do 
    s <- get 
    input <- lift getLine 
    when (annoying input && annoyed s) $ put Angry 

は、それがどのできる、liftIOを使用する方が良いでしょうトランスのスタック全体を持ち上げる。

関連する問題