2016-03-23 3 views
7

Ein Beispiel aus dem wirklichen Leben: Wenn ich gute Laune habe ("guter Zustand"), wenn Manager mich nach Schätzungen fragt, gebe ich ihm eine solide Antwort, wagt es aber, das 3 Mal hintereinander zu tun , ohne ein paar freie Snacks zwischendurch ändert sich meine Laune (ich komme in 'schlechten Zustand') und die nächsten 3 Male, als er sich nähert, bitte ich ihn, mich nicht mit irgendeinem seiner Unsinn zu stören.Warum runState-Signatur hat nur Statusargument?

Hier ist ein Protokoll meiner üblichen Tag:

       [ 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 ] 
... 

Nun ist dieses Modell von mir bei der Arbeit scheint die State Monade zu passen.

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

Aber wie mache ich das? Die Signatur hat Platz für einen Zustand, der in meinem Fall (Mood,Patience) ist, und nicht für die Eingabe (ESTIMATE oder Cookies). Es ist so, als müsste ich antworten ohne zu hören!

Also meine Frage ist: Wie mache ich nicht nur Stateful sondern auch Argumentful Berechnung mit State Monade von Haskell?

Antwort

8

Eine Stateful-Berechnung erhält eine Eingabe, einen Status und gibt eine Ausgabe und einen neuen Status zurück. Also wird der Typ input -> state -> (state, output) sein.

Die runState ist nur eine teilweise angewandte Stateful-Berechnung, die ihre Eingaben bereits übernommen hat.

Beachten Sie auch, dass, wenn Sie Stateful Funktionen zusammensetzen (das heißt, wenn Sie die >>= binden Operator oder do Notation), die Sie tun genau dies: Sie können die Eingänge als Ausdruck liefern und die Bindung ist verantwortlich für die Weitergabe um nur die Zustand.

Sie können get aufrufen, ohne seinen Rückgabewert zu verwenden, aber dann geht es verloren. Wenn Sie davon Gebrauch machen wollen, müssen Sie verwenden und dann die value als explizite Eingabe für die nächste Stateful-Berechnung angeben. Die Bindung kommt nur dann zum Tragen, wenn der Zustand umgangen wird.


Praxisbeispiel: Betrachten Sie die Funktion:

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

Der doStuff Typ hat genau das Muster input -> state -> (state, output). Aber der input Teil wird durch das x Argument dargestellt. Sobald Sie x bereitstellen, erhalten Sie etwas vom Typ state -> (state, output), was genau runState darstellt.

Sie brauchen also nicht die Argumente für stateful Aktionen, weil Sie sie teilweise vorher anwenden können, um "reine stateful Berechnungen, die keine Eingabe haben" (das sind unheimlich Zitate).

2

Es klingt wie das, was Sie suchen nicht State ist aber StateT, ein Monade Transformator die Statusbehaftung zu einem bestehenden Monade hinzufügt.

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

ein Zustand des Typs Gegeben s eine StateT s m a Aktion gibt eine m Aktion, die, wenn ausgeführt, um ein Ergebnis und einen neuen Zustand erzeugt. StateT s ist eine Instanz MonadTrans:

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

Wenn ferner ein mMonad ist, so StateT s m ist.

Also, wenn Sie in einem bestimmten Kontext nehmen „Input“ wollen (zB IO), sind Sie frei, dies zu tun:

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

Für IO es insbesondere in der Regel besser ist liftIO zu verwenden, die können Heben Sie durch einen ganzen Stapel von Transformatoren.