diff --git a/Data/IRC/CLog/Parse.hs b/Data/IRC/CLog/Parse.hs
--- a/Data/IRC/CLog/Parse.hs
+++ b/Data/IRC/CLog/Parse.hs
@@ -133,8 +133,10 @@
   = Time.fromGregorian (2000 + fromIntegral y) m d
 getDay p = error ("cannot parse date from filename: " ++ p)
 
--- | Parse a log file.  The file name (after any directory)
--- is significant.  It is used to set the date for timestamps.
+-- | Parse a log file.
+--
+-- The file name (after any directory) is significant.
+-- It is used to set the date for timestamps.
 -- It should have the form @YY.MM.DD@, as do the files on
 -- @tunes.org@.
 parseLog :: Config -> FilePath -> IO [EventAt]
diff --git a/Data/IRC/Event.hs b/Data/IRC/Event.hs
--- a/Data/IRC/Event.hs
+++ b/Data/IRC/Event.hs
@@ -1,25 +1,38 @@
+{-# LANGUAGE
+    DeriveDataTypeable #-}
+
 -- | Represents events in an IRC channel.
 -- These do not correspond precisely to messages of the IRC
 -- protocol.  They provide a somewhat higher-level view.
 
 module Data.IRC.Event
-  ( Nick   (..)
+  ( -- * Events
+    Nick   (..)
   , Event  (..)
   , EventAt(..)
+
+    -- * Generic events
+  , GenericEvent(..)
+  , decompose
   ) where
 
 import qualified Data.Time as Time
 import qualified Data.Text as T
 
+import Data.Typeable ( Typeable )
+import Data.Data     ( Data, Constr, toConstr )
+
 -- | Event with timestamp.
 data EventAt
   = EventAt Time.UTCTime Event  -- ^ Event with timestamp.
   | NoParse T.Text              -- ^ Unparsable line.
-  deriving (Show, Eq, Ord)
+  deriving (Show, Eq, Ord, Typeable)
+  -- UTCTime lacks Data, so we can't derive it.
+  -- We don't want to force that orphan instance on users.
 
 -- | IRC nicks.
 newtype Nick = Nick T.Text
-  deriving (Show, Eq, Ord)
+  deriving (Show, Eq, Ord, Typeable, Data)
 
 -- | Events in an IRC channel.
 data Event
@@ -35,4 +48,34 @@
   | Log              T.Text  -- ^ Logging started or stopped.
   | Topic            T.Text  -- ^ Topic listing or change.
   | Names            T.Text  -- ^ Users list.
-  deriving (Show, Eq, Ord)
+  deriving (Show, Eq, Ord, Typeable, Data)
+
+-- | For working with @'Event'@s generically.
+--
+-- Indicates the \"subject\" of an event, if any, followed
+-- by other text.
+--
+-- The subject of a @'ReNick'@ event is the old nick.
+data GenericEvent
+  = GenericEvent Constr (Maybe Nick) [T.Text]
+  deriving (Show, Eq, Typeable)
+
+-- | Decompose an @'Event'@ into a @'GenericEvent'@.
+decompose :: Event -> GenericEvent
+decompose x = go x where
+  c = GenericEvent $ toConstr x
+
+  go (Join   n t) = c (Just n) [t]
+  go (Part   n t) = c (Just n) [t]
+  go (Quit   n t) = c (Just n) [t]
+  go (Talk   n t) = c (Just n) [t]
+  go (Notice n t) = c (Just n) [t]
+  go (Act    n t) = c (Just n) [t]
+  go (Mode   n t) = c (Just n) [t]
+
+  go (Log   t) = c Nothing [t]
+  go (Topic t) = c Nothing [t]
+  go (Names t) = c Nothing [t]
+
+  go (ReNick n (Nick t)) = c (Just n) [t]
+  go (Kick a (Nick b) t) = c (Just a) [b,t]
diff --git a/clogparse.cabal b/clogparse.cabal
--- a/clogparse.cabal
+++ b/clogparse.cabal
@@ -1,5 +1,5 @@
 name:                clogparse
-version:             0.1
+version:             0.2
 license:             BSD3
 license-file:        LICENSE
 synopsis:            Parse IRC logs such as the #haskell logs on tunes.org
