2010-10-15 12 views
14

Haskell ist eine reine funktionale Programmiersprache.Haskell und State

Meine Frage ist: Was sind die Vor- und Nachteile der Verwendung von Haskell, um Probleme zu lösen, die viel Staat, zum Beispiel GUI-Programmierung oder Spieleprogrammierung betreffen?

Auch eine zweitrangige Frage: Welche Methoden gibt es, um den Zustand funktional zu handhaben?

Vielen Dank im Voraus.

+0

http://www.haskell.org/all_about_monads/html/statemonad.html –

+4

Handhabungszustand in einer funktionalen Sprache beinhaltet, um die Funktionen, die den Zustand der Umgebung verläuft. Monaden vereinfachen dies. – tylermac

+0

@tylermac Ich konnte nie Monaden verstehen, nun, ich kann nicht sagen, dass ich dumm bin, zumindest bin ich BS in CS, aber Monaden ... kennst du gute Tutorials? – Andrey

Antwort

17

Ich werde zuerst die zweite Frage beantworten. Es gibt viele Möglichkeiten, um den veränderlichen Zustand in Haskell (und anderen FP-Sprachen) zu behandeln. Zuallererst unterstützt Haskell den mutierbaren Zustand in IO durch IORef und mvar Konstrukte. Diese zu verwenden, wird Programmierern aus Imperativsprachen sehr vertraut vorkommen. Es gibt auch spezialisierte Versionen wie STRef und TMVar, sowie veränderbare Arrays, Zeiger und verschiedene andere veränderbare Daten. Der größte Nachteil ist, dass diese in der Regel nur innerhalb von IO oder einer spezialisierten Monade verfügbar sind.

Die gebräuchlichste Methode zum Simulieren des Status in einer funktionalen Sprache besteht darin, den Status explizit als Funktionsargument und zurückgegebenen Wert zu übergeben. Zum Beispiel:

randomGen :: Seed -> (Int, Seed) 

Hier randomGen nimmt einen Samen Parameter und gibt einen neuen Samen. Jedes Mal, wenn Sie es aufrufen, müssen Sie den Seed für die nächste Iteration verfolgen. Diese Technik ist immer für das State-Passing verfügbar, wird aber schnell langweilig.

Wahrscheinlich ist der häufigste Haskell-Ansatz, eine Monade zu verwenden, um diesen Zustand zu verkapseln. Wir können randomGen mit diesem ersetzen:

-- a Random monad is simply a Seed value as state 
type Random a = State Seed a 

randomGen2 :: Random Int 
randomGen2 = do 
    seed <- get 
    let (x,seed') = randomGen seed 
    put seed' 
    return x 

nun alle Funktionen, die ein PRNG benötigen, kann innerhalb des Zufalls Monade laufen um sie verlangen je nach Bedarf. Sie müssen nur einen Anfangszustand und die Berechnung angeben.

(Beachten Sie, dass es Funktionen gibt, die die Definition von randomGen2 erheblich verkürzen; ich wählte die explizitste Version).

Wenn Ihre zufällige Berechnung auch Zugriff auf IO benötigt, verwenden Sie die Monade-Transformer-Version von State, StateT.

Von besonderer Bedeutung ist die ST Monade, die im Wesentlichen einen Mechanismus bietet, IO-spezifische Mutationen vom Rest von IO zu verkapseln. Die ST-Monade stellt STRefs bereit, die eine veränderbare Referenz auf Daten sind, und auch veränderbare Arrays.Mit ST ist es möglich, Dinge wie diese zu definieren:

randomList :: Seed -> [Int] 

wo [Int] ist eine unendliche Liste von Zufallszahlen (es Zyklus werden schließlich je nach PSRG) vom Start Samen geben Sie es.

Schließlich gibt es Functional Reactive Programming. Wahrscheinlich die derzeit bekanntesten Bibliotheken dafür sind Yampa und Reactive, aber die anderen sind es wert, auch zu betrachten. Es gibt verschiedene Ansätze für einen veränderbaren Zustand innerhalb der verschiedenen Implementierungen von FRP; Von meinem leichten Gebrauch von ihnen scheinen sie oft im Konzept einem Signalrahmen ähnlich zu sein wie in QT oder Gtk + (z. B. Hinzufügen von Zuhörern für Ereignisse).

Jetzt für die erste Frage. Der größte Vorteil für mich ist, dass der änderbare Zustand auf Code-Ebene von anderem Code getrennt ist. Dies bedeutet, dass der Code den Status nicht versehentlich ändern kann, es sei denn, dies wird ausdrücklich in der Typsignatur erwähnt. Es gibt auch eine sehr gute Kontrolle des Nur-Lese-Zustands gegenüber dem Veränderlichen-Zustand (Leser-Monade vs. Zustands-Monade). Ich finde es sehr nützlich, meinen Code auf diese Weise zu strukturieren, und es ist nützlich, nur anhand der Typ-Signatur zu erkennen, ob eine Funktion unerwartet in den Status mutieren könnte.

Ich persönlich habe keine Vorbehalte gegenüber der Verwendung von veränderlichen Zustand in Haskell. Die größte Schwierigkeit ist, dass es mühsam sein kann, etwas hinzuzufügen, das es vorher nicht benötigt hat, aber das Gleiche wäre in anderen Sprachen, die ich für ähnliche Aufgaben verwendet habe (C#, Python), mühsam.

+0

Was ist 'Seed' hier? Ich habe versucht, es als int am Anfang meiner Datei zu definieren ('type Seed = Int'), aber bekomme den Fehler' No instance for (Show (Random Int)) '(Ich habe zunächst die Theorie deines Codes benutzt, was nicht der Fall war Arbeit, so versuchte Kopieren-Einfügen alles in und bekam diesen Fehler dann). –

+0

Dies ist mehr Pseudocode als eine tatsächliche Implementierung. 'Seed' ist ein abstrakter Typ, der die zustandsabhängigen Daten des PRNG darstellt. Ich benutzte PNRG, weil es ein bekanntes Beispiel für eine Klasse von Stateful-Algorithmen ist. Wenn Sie einen Zufallszahlengenerator benötigen, würde ich ein Paket wie 'mwc-random' oder' mersenne-random' empfehlen. –

+0

Danke für die Antwort - ich versuche wirklich moads zu verstehen, anstatt einen PRNG zu benutzen, aber ich denke, das ist ein wirklich gutes Beispiel. Ich bin jedoch total festgefahren. Ich habe vor ein paar Stunden eine SO-Frage gestellt, bin aber jetzt noch verwirrter! http://stackoverflow.com/questions/23595363/simple-haskell-monad-random-number/ –

2

Normalerweise würden Sie einen Monad Transformer mit einem StateT und einem IO verwenden, weil die Ansicht (GUI) IO benötigt, um zu reagieren, aber sobald Sie Ihren Monad Transformer in einer newtype definiert haben, möchten Sie Machen Sie die Signaturen der Spiellogik nur mit der MonadState Schnittstelle, auf diese Weise haben Sie immer noch den Vorteil von Nicht-IO-ness-Änderungen. Code unten zu erklären, was ich meine:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
import Control.Monad.State 

data GameState = GameState { ... } deriving (Show) 
newtype GameMonad a = GameMonad (StateT GameState IO a) 
         deriving (Monad, MonadState GameState, MonadIO) 

-- This way, now you have a monad with the state of the info 
-- what would you like now is being able to modify the state, without actually 
-- having IO capabilites. 

movePlayerOnState :: (MonadState GameState) m => Position -> m() 
-- In this function we get the state out of the state monad, and then we modify 
-- with a pure function, then put the result back again 

-- Other times you would like to have the GUI API available, requiring the IO monad 
-- for that 
renderGameFromState :: MonadIO m => GameState -> m() 
-- in this method you would use liftIO method to call the GUI API 

Dieser Code ist ziemlich komplex, wenn Sie Monaden nicht verstehen, aber meine Faustregel, was der Staat Monad für verstehen herauszufinden, was Monad Transformers sind (ohne die Notwendigkeit, zu verstehen, wie sie funktionieren) und wie man die StateT-Monade benutzt.

Ich kann Ihnen zu einem Sokoban-Projekt verweisen ich mit anderen Mitspieler tat die nützlich sein könnten, verwendet es ncurses als GUI, aber man kann die Idee der Logik bekommen und wie wir es geschafft Staaten auf dem Spiel

http://github.com/roman/HaskBan

Viel Glück.

4

Was sind die Vor- und Nachteile der Verwendung von Haskell, um Probleme zu lösen, die viele Zustände betreffen, z. B. GUI-Programmierung oder Spieleprogrammierung?

Der Vorteil ist, dass, auch wenn Sie nicht besonders sind die Vorteile der Reinheit nehmen, Haskell einfach eine gute Sprache ist.

First-Class-Funktionen - sollte 2010 keine große Sache sein, aber es ist. Algebraische Typen mit Mustervergleich. Leistungsstarke statische Typprüfung mit Typinferenz. Saubere Syntax. Erstklassiger Nebenläufigkeit, STM und Thread-freie reine Parallelität. Ein guter Compiler. Tonnenweise Bibliotheken und mehr jeden Tag. Eine aktive, hilfreiche Gemeinschaft.

Dies sind keine großen ideologischen Entscheidungen wie Reinheit oder Faulheit. Sie sind nur gute Ideen.Sie sind Dinge, die die meisten Sprachen haben können, aber zu viele nicht.

+0

Können Sie mir sagen, was mit erstklassiger Nebenläufigkeit gemeint ist? –

4

Ein Zustand Monade ist über die schlimmste Art und Weise eine grafische Benutzeroberfläche oder ein Spiel in Haskell zu modellieren. Ich denke, die zweitbeste Option besteht darin, in beiden Fällen Gleichzeitigkeit zu verwenden. Die beste Option wurde jedoch von Paul erwähnt: Funktionale reaktive Programmierung (FRP).

persönlich plädiere ich arrowized FRP (AFK), die ich denke, zuerst als Yampa und später gegabelt als etwas nützlicher Animas umgesetzt wurde. Jedoch erreicht Yampa schnell sein Limit, also habe ich eine leistungsfähigere, ausdrucksstärkere Bibliothek geschrieben, die netwire genannt wird, die auch einige konzeptionelle Verbesserungen gegenüber den früheren zwei hat.

In seinem Wesen AFRP ist ein Funktionszustand System. Es ist funktionsfähig, da dieser Zustand nicht als veränderliche Variablenwerte, sondern als mutierende Funktionen modelliert wird. Dies ist sauberer und erfordert keine imperative Programmierung wie Zustands-Monaden.