Just wondering, could type families be of any help here?
I don't know type families, but can it be a mean to regroup together the event types, that are now completely separated :
data NewPlayer deriving Typeable
data NewRule deriving Typeable



On Fri, Jun 15, 2012 at 10:59 PM, Corentin Dupont <corentin.dupont@gmail.com> wrote:
I made some modifications based on your suggestions (see below).
I made a two parameters class:
class (Typeable e, Typeable d) => Handled e d
Because after all what I want is to associate an event with its type parameters.
I don't know why I cannot implement you suggestion to restrict the instances of Event:
data (Handled e d) => Event e = Event deriving (Typeable, Eq)
gives me a
Not in scope: type variable `d'

But apart from that it works very well! It's quite a nice interface!
Also just to know, is there a way of getting ride of all these "Typeable"?

{-# LANGUAGE ExistentialQuantification, DeriveDataTypeable, MultiParamTypeClasses #-}

module Events (addEvent, newPlayer, newRule) where

import Control.Monad
import Data.List
import Data.Typeable


newtype Player = P Int deriving Typeable
newtype Rule = R Int deriving Typeable
data Event e = Event deriving (Typeable, Eq)

data NewPlayer deriving Typeable
data NewRule deriving Typeable

newPlayer :: Event NewPlayer
newPlayer = Event
newRule :: Event NewRule
newRule = Event

class (Typeable e, Typeable d) => Handled e d
instance Handled NewPlayer Player
instance Handled NewRule Rule

data EventHandler = forall e d . (Handled e d) => EH (Event e) (d -> IO ())

addEvent :: (Handled e d) => Event e -> (d -> IO ()) -> [EventHandler] -> [EventHandler]
addEvent e h ehs = (EH e h):ehs
 
triggerEvent :: (Handled e d) => Event e -> d -> [EventHandler] -> IO ()

triggerEvent e d ehs = do
    let r = find (\(EH myEvent _) -> cast e == Just myEvent) ehs
    case r of
       Nothing -> return ()
       Just (EH _ h) -> case cast h of

        Just castedH -> castedH d
        Nothing -> return ()

-- TESTS

h1 :: Player -> IO ()
h1 (P a) = putStrLn $ "Welcome Player " ++ (show a) ++ "!"
h2 :: Rule -> IO ()
h2 (R a) = putStrLn $ "New Rule " ++ (show a)
eventList1 = addEvent newPlayer h1 []
eventList2 = addEvent newRule h2 eventList1

trigger1 = triggerEvent newPlayer (P 1) eventList2 --Yelds "Welcome Player 1!"
trigger2 = triggerEvent newRule (R 2) eventList2 --Yelds "New Rule 2"




On Fri, Jun 15, 2012 at 4:53 PM, Alexander Solla <alex.solla@gmail.com> wrote:


On Fri, Jun 15, 2012 at 6:38 AM, Corentin Dupont <corentin.dupont@gmail.com> wrote:

It just bothers me a little that I'm not able to enumerate the events, and also that the user is able to create events with wrong types (like New :: Event String), even if they won't be able to register them.

This can be solved with an explicit export list/smart constructors.

newPlayer :: Event Player
newRule    :: Event Rule
(hide the New constructor)

In any case, my thinking was that your original

data Event = NewPlayer | NewRule

was basically trying to "join" the semantics of "new things" with Player and Rule.  But the original approach ran into the problem you mention below -- it is difficult to maintain invariants, since the types want to "multiply".  So formally, I factored:

data Event = NewPlayer | NewRule ==>
data Event = New (Player | Rule)    ==>
data Event d = New -- (since the original event didn't want a Player or Rule value.  It witnessed the type relation)

On the other hand, if you want to make sure that a type must be "Handled" before you can issue an Event, you can do:

data (Handled d) => Evend d = New

I'm pretty sure the compiler will complain if you try to make a (New :: Event String).  I like this idea better than smart constructors for events, if only because you get to use ScopedTypeVariables.
 
I also have several unrelated events that use the same type of data, so this would be a problem.

Can you clarify?

I mean that I have events like:
Message String
UserEvent String
That have a "data" of the same type, but they are not related.

 
 
Adding more events like
data Event d = NewPlayer | NewRule deriving (Typeable, Eq)
is not correct because I can add wrong events like:
addEvent (NewPlayer :: Event Rule) (H(undefined::(Rule -> IO()))) []

Also one question: I don't understand the "where" clause in your class. If I remove it, it works the same...

Yes, unnecessary where clauses are optional. 

Here is my code:

newtype Player = P Int deriving Typeable
newtype Rule = R Int deriving Typeable
data Event d = New deriving (Typeable, Eq)

class (Typeable d) => Handled d where
data Handler d = H (d -> IO ())

data EventHandler = forall d . (Handled d) => EH (Event d) (Handler d)


instance Handled Player
instance Handled Rule

addEvent :: (Handled d) => Event d -> Handler d -> [EventHandler] -> [EventHandler]
addEvent e h ehs = (EH e h):ehs

 
triggerEvent :: (Handled d) => Event d -> d -> [EventHandler] -> IO ()
triggerEvent e d ehs = do
    let r = find (\(EH myEvent _) -> cast e == Just myEvent) ehs

    case r of
       Nothing -> return ()
       Just (EH _ (H h)) -> case cast h of
        Just castedH -> castedH d
        Nothing -> return ()

h1 :: Player -> IO ()
h1 (P a) = putStrLn $ "Welcome Player " ++ (show a) ++ "!"
h2 :: Rule -> IO ()
h2 (R a) = putStrLn $ "New Rule " ++ (show a)
eventList1 = addEvent (New :: Event Player) (H h1) []
eventList2 = addEvent (New :: Event Rule) (H h2) eventList1

trigger1 = triggerEvent (New :: Event Player) (P 1) eventList2 -- yelds "Welcome Player 1!"
trigger2 = triggerEvent (New :: Event Rule) (R 2) eventList2 --yelds "New Rule
2"

Best,
Corentin


On Fri, Jun 15, 2012 at 12:40 AM, Alexander Solla <alex.solla@gmail.com> wrote:


On Thu, Jun 14, 2012 at 2:04 PM, Corentin Dupont <corentin.dupont@gmail.com> wrote:
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


Right, okay.  Heterogenous lists are tricky, but I think we can get away with using ExistentialQuantification, since you seem to only want to dispatch over the heterogenous types.  The assumption I made is a big deal!  It means you can't extract the d value.  You can only apply properly typed functions (your handlers) on it.
 
{-# LANGUAGE ExistentialQuantification #-} 

type Player = Int

type Rule = Int

data Event d = New d


class Handled data where -- Together with EventHandler, corresponds to your "Data" type

data EventHandler = forall d . (Handled d) => EH (Event d) (d -> IO ()) -- EventHandler takes the place of your (Event d, Handler d) pairs without referring to d.


instance Handled Player
instance Handled Rule


addEvent :: (Handled d) => Event d -> Handler d -> [EventHandler] -> [EventHandler] -- Every [EventHandler] made using addEvent will be of "correct" types (i.e., preserve the typing invariants you want), but YOU must ensure that only [EventHandler]s made in this way are used.  This can be done statically with another type and an explicit export list.  We can talk about that later, if this works in principle.
 
 
triggerEvent :: (Handled d) => Event d -> d -> [EventHandler] -> IO ()