Also schreibe ich diese kleine soccer game für einige Zeit, und da ist eine Sache, die mich von Anfang an nervt. Das Spiel folgt dem Yampa Arcade Muster, so gibt es eine Summe Typ für die „Objekte“ im Spiel:Kann der Typ-Checker mir hier helfen? Mit Typfamilien, vielleicht?
data ObjState = Ball Id Pos Velo
| Player Id Team Number Pos Velo
| Game Id Score
Objekte reagieren auf Nachrichten, so dass es eine andere Art Summe:
data Msg = BallMsg BM
| PlayerMsg PM
| GameMsg GM
data BM = Gained | Lost
data PM = GoTo Position | Shoot
data GM = GoalScored | BallOutOfBounds
Der Yampa Rahmen stützt auf sogenannten Signalfunktionen. In unserem Fall gibt es Signalfunktionen für Ball-, Spieler- und Spielverhalten. Grob vereinfacht:
ballObj, playerObj, gameObj :: (Time -> (GameInput, [Msg]))
-> (Time -> (ObjState, [(Id, Msg)]))
Also z.B. ballObj nimmt eine Funktion, die den GameInput (Tastenanschläge, Spielstatus, ...) und eine Liste von Nachrichten für den Ball zu einem bestimmten Zeitpunkt liefert, und gibt eine Funktion zurück, die den Ballstatus und seine Nachrichten an andere Objekte (Ball) liefert , Spiel, Spieler) zu jeder Zeit. In Yampa sucht die Art Unterschrift tatsächlich ein wenig schöner:
ballObj, playerObj, gameObj :: SF (GameInput, [Msg]) (ObjState, [(Id, Msg)])
Diese einheitliche Art Signatur ist wichtig für den Yampa Rahmen: (wieder, sehr grob vereinfacht) baut eine große Signalfunktion aus einer Liste von 11 + 11 (Spieler) + 1 (Ball) + 1 (Spiel) Signal funktioniert mit dem gleichen Typ (über dpSwitch), den es dann ausführt (über Reaktivierung).
So was jetzt Bugs: Es macht nur Sinn, eine BallMsg zu einem Ball oder eine PlayerMsg zu einem Player zu senden. Wenn jemand zum Beispiel eine GameMsg zu einem Ball sendet, wird das Programm abstürzen. Gibt es keine Möglichkeit, den Typprüfer in Position zu bringen, um dies zu vermeiden? Ich habe vor kurzem diesen netten Pokemon Post auf Typ Familien gelesen, und es scheint, als gäbe es eine Analogie. Vielleicht könnte dies ein Ausgangspunkt sein:
class Receiver a where
Msg a :: *
putAddress :: Msg a -> a -> Msg a
data BallObj = ...
data GameObj = ...
data PlayerObj = ...
instance Receiver BallObj where
Msg BallObj = Gained | Lost
(...)
Nun wird die SF-Funktion wie folgt aussehen könnte:
forall b . (Receiver a, Receiver b) => SF (GameInput, [Msg a]) (a, [(b, Msg b)])
Wird das mich überall bekommen?
Entspannen Sie sich. Haskell-Programme können aufgrund von Typfehlern nicht abstürzen (es sei denn, Sie verwenden unsicheren oder fremden Code). Haben Sie Arbeitscode, der das Problem aufzeigt? –
Es stürzt wegen nicht erschöpfender Muster in den Funktionen ab, die die Nachrichten verarbeiten. – martingw
Ah, diese Art von Crash. –