2013-05-22 14 views
7

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?

+0

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? –

+3

Es stürzt wegen nicht erschöpfender Muster in den Funktionen ab, die die Nachrichten verarbeiten. – martingw

+0

Ah, diese Art von Crash. –

Antwort

1

Gerade auf den ersten Blick fällt ein großes Problem mit Ihrem Design auf: Sie vereinen völlig unterschiedliche Entitäten Ball, Player und Game unter einem einzigen Typ. Wenn Sie eine Union-Typ über diesen Unternehmen brauchen, gehen Sie auf die gleiche Weise mit den Nachrichten haben, indem sie getrennte Arten zu machen, das heißt:

data AnyObject = AnyObjectBall Ball 
       | AnyObjectPlayer Player 
       | AnyObjectGame Game 

Auf diese Weise werden Sie auch beide die spezifischen Funktionen (Ball -> BallMsg -> ...) zum Ausdruck bringen und allgemeine (AnyObject -> AnyMsg -> ...).

Aber wenn ich dein Problem richtig verstanden habe, glaube ich, eine Lösung für Sie haben, die nicht Vereinigung Typen benötigt:

class Signal object message where 
    signal :: SF (GameInput, [message]) (object, [(Id, message)]) 

data Ball = Ball Id Pos Velo 
data BallMsg = BallMsgGained | BallMsgLost 
instance Signal Ball BallMsg where 
    -- ... 

-- so on for Player and Game 
+0

Danke, es sollte etwas in dieser Richtung sein. Es muss jedoch irgendeine Art von funktionaler Abhängigkeit oder Typ-Familienbeziehung zwischen Objekt und Nachricht geben, und ich denke, dass ich die Nachricht mit dem empfangenden Objekt im Ergebnis des SF tupeln muss. Es funktioniert vielleicht immer noch nicht oder sieht sehr ungeschickt aus, denn am Ende des Tages hat die Nachrichtengenerierung und -versendung eine dynamische Natur, die sowieso nicht zur statischen Typisierung passt. Wenn ich jemals etwas schaffe, werde ich es hier teilen! – martingw

+0

@martingw Wenn der Nachrichtentyp oder das Objekt in Runtime definiert wird, können Ihnen keine fundeps, type-families oder andere typenspezifische Programmierfunktionen helfen. In diesem Szenario schlage ich vor, den Mustervergleich mit Funktionen vom Typ AnyObject -> AnyMsg -> ... 'beizubehalten. –

+0

Ich denke, es gibt eine Chance für eine statische Eingabe: Zum Beispiel misst der Ball SF ständig die Ballposition, und wenn der Ball außerhalb der Grenzen ist, kann er eine "Out of Bounds" Nachricht an das Spiel SF senden, und ein " du bist nicht mehr im Besitz "Nachricht an den Spieler, der gerade den Ball hat. Sagen Sie die ID des Spiels ist 2 und die ID des Spielers ist 4, dann gibt der Ball SF eine Liste [(2, OutOfBounds), (4, DropPossession)] zurück. Und wenn ich die Zahlen verwechsle, stürzt das Programm ab. Es könnte etwas wie [(Game, OutOfBounds), (Player, DropPossession)] mit einem fundep zwischen Objekt und Nachrichtentyp ... – martingw

2

Das Yampa-Arcade-Papier zu glätten, scheint, als ob Sie eine route Funktion haben, die von ihrem Beispiel gezeichnet wird.

Mein Vorschlag wäre Sie ändern route, so dass es nicht eine einzige Liste von Objekten, sondern ein einzelnes Spielobjekt, ein einzelnes Ball-Objekt und eine Sammlung von Spieler-Objekten. Dann haben

data BallMsg = ... 
data PlayerMsg = ... 
data GameMsg = ... 

data AnyMsg = ABallMsg BallMsg 
      | APlayerMsg PlayerMsg 
      | AGameMsg GameMsg 

Jetzt route Arbeiten auf einer einheitlichen AnyMsg aber es löst sie an das richtige Ziel abhängig von ihrem Inhalt.

+0

Danke für die Eingabe! Mein Code ist bereits organisiert, wie Sie vorschlagen, das Problem ist, dass ich nicht den Typ-Checker zu illegalen Objekt/MSG-Kombinationen zur Kompilierzeit zu verhindern bekommen. Ich denke, ich muss meine Frage neu formulieren, ich kann sehen, es ist nicht wirklich klar, was ich will. – martingw

Verwandte Themen