So, I have been writing this little football game for some time, and there is one thing that bothers me from the very beginning. The game follows the Yampa Arcade template , so there is a sum type for the "objects" in the game:
data ObjState = Ball Id Pos Velo | Player Id Team Number Pos Velo | Game Id Score
Objects respond to messages, so there is a different type of sum:
data Msg = BallMsg BM | PlayerMsg PM | GameMsg GM data BM = Gained | Lost data PM = GoTo Position | Shoot data GM = GoalScored | BallOutOfBounds
The structure of Yampa is based on the so-called signaling functions. In our case, there are signal functions for playing ball, player and game. Roughly simplified:
ballObj, playerObj, gameObj :: (Time -> (GameInput, [Msg])) -> (Time -> (ObjState, [(Id, Msg)]))
So, for example, ballObj accepts a function that gives the GameInput (key strokes, game state, ...) and a list of messages specifically for the ball at any given time and returns a function that gives the state of the ball, and messages to other objects (ball, game , players) at any given time. In Yampa, a type signature actually looks a little nicer:
ballObj, playerObj, gameObj :: SF (GameInput, [Msg]) (ObjState, [(Id, Msg)])
This formatted signature is important for the Yampa framework: (again, very roughly simplified), it creates a large signal function from the list 11 + 11 (players) + 1 (ball) + 1 (game) signal with functions of the same type (via dpSwitch) which is then launched (via reagent).
So, what bothers me: it makes sense to send BallMsg to Ball or PlayerMsg to a player. If someone ever sends, for example, GameMsg to Ball, the program will crash. Isn't there a way to get a type controller in position to avoid this? I recently read this nice Pokemon post about family types, and it looks like there is some analogy. Perhaps this could be the starting point:
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 (...)
Now the SF function might look something like this:
forall b . (Receiver a, Receiver b) => SF (GameInput, [Msg a]) (a, [(b, Msg b)])
Will it get me anywhere?