That look really nice!
Unfortunately I need to have an heterogeneous list of all events with their handlers.
With this test code it won't compile:
test1 = addEvent (New :: Event Player) (H (undefined::(Player -> IO ()))) []
test2 = addEvent (New :: Event Rule) (H (undefined::(Rule -> IO ()))) test1
On Thu, Jun 14, 2012 at 12:15 PM, Corentin Dupont <corentin.dupont@gmail.com> wrote:
Hi folks,
I'm trying to make a simple event driven engine. It simply consists of two functions:
- "addEvent", where you pass the event name with a callback,
- "triggerEvent" where you pass the event name with the data.
the data shall be passed to the callback of the corresponding event.
I have trouble making it correctly typed.
Here is my try:
type Player = Int --dummy types for the example
type Rule = Int
data EventEnum = NewPlayer | NewRule deriving Eq
data Data = P Player | R Rule
data Handler = H (Data -> IO ())
addEvent :: EventEnum -> Handler -> [(EventEnum, Handler)] -> [(EventEnum, Handler)]
addEvent e h es = (e,h):es
triggerEvent :: EventEnum -> Data -> [(EventEnum, Handler)] -> IO ()
triggerEvent e d es = do
let r = lookup e es
case r of
Nothing -> return ()
Just (H h) -> h d
The trouble is that I want the user to be only able to add an event that is compatible with its handler:
For example the event NewPlayer should have a handler of type Player -> IO (). The data passed when triggering this event should be only of type Player.
How can I do that? It sound like dependant typing...Haven't tried it, and I don't know if it actually does what you want in the big picture. But you can do "dynamic" dependent typing with dummy (free) type variables.type Player = Int --dummy types for the exampledata Event d = New deriving Eq -- not necessary for this example, but you might want to enumerate other events.
type Rule = Int
class Handled data where -- Corresponds to your "Data" type
data Handler d = H (d -> IO ())instance Handled Playerinstance Handled RuleaddEvent :: (Handled d) => Event d -> Handler d -> [(Event d, Handler d)] -> [(Event d, Handler)]triggerEvent :: (Handled d) => Event d -> d -> [(Event d, Handler d)] -> IO ()Basically, this means that Events are "keyed" into separate spaces by the Handled types. (New :: Event Player) has a different type as (New :: Event Rule).You might want to look into ScopedTypeVariables.