diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+		   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,7 @@
+This is a low-level Haskell FFI interface to epoll, a Linux specific I/O
+notification facility. For usage examples please have a look into the 
+examples directory (please compile with -threaded option). 
+
+This library has been developed and tested with GHC 6.10.4 and 
+Linux 2.6.28-15 (x86).
+
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import Distribution.Simple
+
+main = defaultMain
+
diff --git a/epoll.cabal b/epoll.cabal
new file mode 100644
--- /dev/null
+++ b/epoll.cabal
@@ -0,0 +1,27 @@
+name:               epoll
+version:            0.1
+synopsis:           Low-level bindings to epoll.
+description:        Bindings to epoll, a Linux specific I/O
+                    event notication facility (cf. man epoll(7)).
+category:           sys
+license:            LGPL
+license-file:       LICENSE
+author:             Toralf Wittner
+maintainer:         toralf.wittner@gmail.com
+copyright:          Copyright (c) 2009 Toralf Wittner
+stability:          experimental
+tested-With:        GHC == 6.10.4
+cabal-version:      >= 1.6.0
+build-type:         Simple
+extra-source-files: README, examples/*.hs
+
+library
+  exposed-modules:  System.Linux.Epoll,
+                    System.Linux.EpollM,
+                    System.Linux.Epoll.Base
+  other-modules:    Util
+  ghc-options:      -Wall -O2
+  extensions:       CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving
+  build-depends:    base >= 3 && < 5, unix, mtl
+  hs-source-dirs:   src
+
diff --git a/examples/Server.hs b/examples/Server.hs
new file mode 100644
--- /dev/null
+++ b/examples/Server.hs
@@ -0,0 +1,77 @@
+module Main where
+
+import System.Linux.EpollM
+import System.Posix.IO
+import System.Posix.Types
+import System.Environment
+import System.IO
+import Data.Maybe
+import Text.Printf
+import Control.Monad
+import Control.Monad.Trans
+import System.Posix.Signals
+import Network.Socket
+
+main :: IO ()
+main = getAndVerifyArgs >>= start . head
+
+start :: String -> IO ()
+start p = runEpollBig_ $ do
+
+    defaultDispatchLoop_ readRequest
+
+    ai <- liftIO $ getAddrInfo (Just (defaultHints {addrFlags = [AI_PASSIVE]}))
+                               Nothing 
+                               (Just p)
+
+    let serveraddr = head ai
+
+    -- create server socket
+    sock <- liftIO $ socket (addrFamily serveraddr) Stream defaultProtocol
+    liftIO $ setSocketOption sock ReuseAddr 1
+
+    -- bind it to the address we're listening to
+    liftIO $ bindSocket sock (addrAddress serveraddr)
+
+    liftIO $ printf "Listening at port %s\n" p
+
+    -- start listening for connection requests max. connection requests waiting
+    -- to be accepted = 5 (max. queue size)
+    liftIO $ listen sock 256
+
+    -- Ignore broken pipes
+    liftIO $ installHandler sigPIPE Ignore Nothing
+
+    -- accept connection requests (CTRL-C to abort)
+    forever $ do
+        (connsock, clientaddr) <- liftIO $ accept sock
+        procRequest connsock clientaddr
+ where
+    procRequest :: Socket -> SockAddr -> Epoll ()
+    procRequest sock' _ = do
+        let fd = Fd $ fdSocket sock'
+        liftIO $ setFdOption fd NonBlockingRead True
+        add sock' [inEvent, peerCloseEvent, oneShotEvent] fd
+        return ()
+
+readRequest :: Event Socket -> Epoll ()
+readRequest e = fork_ $ do
+    let et = eventType e
+        fd = eventFd e
+    liftIO $ printf "event=%s, fd=%s\n" (show et) (show fd)
+    if et =~ peerCloseEvent
+        then do liftIO $ putStrLn "peer closed"
+                delete (eventDesc e)
+                liftIO $ sClose (eventRef e)
+        else do (s, _) <- liftIO $ fdRead fd 2048
+                modify [inEvent, peerCloseEvent, oneShotEvent] (eventDesc e)
+                liftIO $ printf "readRequest: %s\n" s
+
+getAndVerifyArgs :: IO [String]
+getAndVerifyArgs = do
+    args <- getArgs
+    when (length args /= 1)
+         (error "Usage: Server <port>")
+    return args
+
+
diff --git a/examples/Stdin.hs b/examples/Stdin.hs
new file mode 100644
--- /dev/null
+++ b/examples/Stdin.hs
@@ -0,0 +1,28 @@
+module Main where
+
+import System.Linux.Epoll
+import System.Posix.IO
+import System.IO
+import Data.Maybe
+import Text.Printf
+
+eventTypes :: [EventType]
+eventTypes = [inEvent, edgeTriggeredEvent, oneShotEvent]
+
+callback :: Device -> Event a -> IO ()
+callback d e = do
+    let et = eventType e
+        fd = eventFd e
+    setFdOption fd NonBlockingRead True
+    printf "event=%s, fd=%s\n" (show et) (show fd)
+    (s, c) <- fdRead fd 16
+    printf "read %s byte(s): %s\n" (show c) s
+    modify d eventTypes (eventDesc e)
+
+main :: IO ()
+main = do
+    let s = fromJust $ toSize 32
+    dev <- create s
+    add dev () eventTypes stdInput
+    defaultDispatchLoop (callback dev) dev
+
diff --git a/examples/StdinM.hs b/examples/StdinM.hs
new file mode 100644
--- /dev/null
+++ b/examples/StdinM.hs
@@ -0,0 +1,28 @@
+module Main where
+
+import Text.Printf
+import Control.Monad.Reader
+import System.IO
+import System.Posix.IO
+import System.Linux.EpollM
+
+eventTypes :: [EventType]
+eventTypes = [inEvent, edgeTriggeredEvent, oneShotEvent]
+
+callback :: Event a -> Epoll ()
+callback e = do
+    let fd = eventFd e
+        et = eventType e
+        ds = eventDesc e
+    liftIO $ do
+        setFdOption fd NonBlockingRead True
+        printf "event=%s, fd=%s\n" (show et) (show fd)
+        (s, c) <- fdRead fd 16
+        printf "read %s byte(s): %s\n" (show c) s
+    modify eventTypes ds
+
+main :: IO ()
+main = runEpollSmall_ $ do
+    add_ eventTypes stdInput
+    defaultDispatchLoop callback
+
diff --git a/src/System/Linux/Epoll.hs b/src/System/Linux/Epoll.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Linux/Epoll.hs
@@ -0,0 +1,29 @@
+-- |
+-- Module      : System.Linux.Epoll
+-- Copyright   : (c) 2009 Toralf Wittner
+-- License     : LGPL
+-- Maintainer  : toralf.wittner@gmail.com
+-- Stability   : experimental
+-- Portability : non-portable
+
+module System.Linux.Epoll (
+    module System.Linux.Epoll.Base,
+
+    dispatchLoop,
+    defaultDispatchLoop
+
+) where
+
+import Data.Maybe
+import Control.Monad
+import System.Linux.Epoll.Base
+
+-- | Repeatedly waits for epoll events and invokes the given callback function
+-- with each event occured.
+dispatchLoop :: Duration -> (Event a -> IO ()) -> Device -> IO ()
+dispatchLoop dur f dev = forever $ wait dur dev >>= mapM_ (\e -> f e)
+
+-- | Like 'dispatchLoop' but uses a default 'Duration' of 500ms.
+defaultDispatchLoop :: (Event a -> IO ()) -> Device -> IO ()
+defaultDispatchLoop = dispatchLoop (fromJust $ toDuration 500)
+
diff --git a/src/System/Linux/Epoll/Base.hsc b/src/System/Linux/Epoll/Base.hsc
new file mode 100644
--- /dev/null
+++ b/src/System/Linux/Epoll/Base.hsc
@@ -0,0 +1,235 @@
+{-# LANGUAGE CPP, ForeignFunctionInterface #-}
+-- |
+-- Module      : System.Linux.Epoll.Base
+-- Copyright   : (c) 2009 Toralf Wittner
+-- License     : LGPL
+-- Maintainer  : toralf.wittner@gmail.com
+-- Stability   : experimental
+-- Portability : non-portable
+-- 
+-- Low level interface to Linux' epoll, a high performance polling mechanism
+-- which handles high numbers of file descriptors efficiently. See man epoll(7)
+-- for details.
+
+module System.Linux.Epoll.Base (
+
+    EventType,
+    Size,
+    toSize,
+    Duration,
+    toDuration,
+    Descriptor,
+    Device,
+    Event (eventFd, eventType, eventRef, eventDesc),
+    (=~),
+
+    create,
+    close,
+    wait,
+
+    add,
+    modify,
+    delete,
+
+    inEvent,
+    outEvent,
+    peerCloseEvent,
+    urgentEvent,
+    errorEvent,
+    hangupEvent,
+    edgeTriggeredEvent,
+    oneShotEvent
+
+) where
+
+import Util
+import Foreign
+import Foreign.C.Types (CInt)
+import Foreign.C.Error (throwErrnoIfMinus1,
+                        throwErrnoIfMinus1_,
+                        throwErrnoIfMinus1Retry)
+import Foreign.Marshal.Array (peekArray, mallocArray)
+import Foreign.Marshal.Utils (with)
+import System.Posix.Types (Fd (Fd))
+import System.Posix.Signals (installHandler, sigPIPE, Handler (Ignore))
+
+#include <sys/epoll.h>
+
+-- | EventType corresponds to epoll's event type defines, e.g. EPOLLIN,
+-- EPOLLOUT, EPOLLET, etc.
+newtype EventType = EventType { fromEventType :: Int } deriving (Eq, Ord)
+
+-- | Operation corresponds to epoll's EPOLL_CTL_ADD et al. It is the operation
+-- designation in epoll_ctl.
+newtype Operation = Operation { fromOp :: Int } deriving (Eq, Ord)
+
+-- | Unsigned type used for length specifications.
+newtype Size = Size { fromSize :: Word32 } deriving (Eq, Ord, Show)
+
+-- | Unsigned type used for timeout specifications.
+newtype Duration = Duration { fromDuration :: Word32 } deriving (Eq, Ord, Show)
+
+-- | Event descriptor. Will be returned from 'add' and must be passed to
+-- 'delete' exactly once.
+newtype Descriptor a = Descriptor { descrPtr :: StablePtr (Fd, a) }
+
+-- | Abstract epoll device. Holds internal data. Returned from 'create' and used
+-- in almost every other API function. Must be closed explicitely with 'close'.
+data Device = Device {
+        deviceFd      :: !Fd,                -- ^ The epoll file descriptor.
+        eventArray    :: !(Ptr EventStruct), -- ^ The event array used in 'wait'.
+        eventArrayLen :: !Size               -- ^ The size of the event array.
+    } deriving (Eq, Show)
+
+-- | A single event ocurrence.
+data Event a = Event {
+        eventFd   :: !Fd,        -- ^ The file descriptor.
+        eventType :: !EventType, -- ^ The event type.
+        eventRef  :: !a,         -- ^ The user data as given in 'add'.
+        eventDesc :: !(Descriptor a)
+    }
+
+-- | Representation of struct epoll_event.
+data EventStruct = EventStruct {
+        epollEvents :: !Word32,
+        epollData   :: !(Ptr ())
+    }
+
+instance Storable EventStruct where
+    alignment _ = #{alignment struct epoll_event}
+    sizeOf _ = #{size struct epoll_event}
+    peek p = do
+        evts <- #{peek struct epoll_event, events} p
+        dat  <- #{peek struct epoll_event, data} p
+        return (EventStruct evts dat)
+    poke p (EventStruct evts dat) = do
+        #{poke struct epoll_event, events} p evts
+        #{poke struct epoll_event, data} p dat
+
+#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)
+
+#{enum EventType, EventType,
+    inEvent = EPOLLIN,
+    outEvent = EPOLLOUT,
+    peerCloseEvent = EPOLLRDHUP,
+    urgentEvent = EPOLLPRI,
+    errorEvent = EPOLLERR,
+    hangupEvent = EPOLLHUP,
+    edgeTriggeredEvent = EPOLLET,
+    oneShotEvent = EPOLLONESHOT
+}
+
+instance Show EventType where
+    show e | e == inEvent = "EPOLLIN"
+           | e == outEvent = "EPOLLOUT"
+           | e == peerCloseEvent = "EPOLLRDHUP"
+           | e == urgentEvent = "EPOLLPRI"
+           | e == errorEvent = "EPOLLERR"
+           | e == hangupEvent = "EPOLLHUP"
+           | e == edgeTriggeredEvent = "EPOLLET"
+           | e == oneShotEvent = "EPOLLONESHOT"
+           | otherwise = show $ fromEventType e
+
+#{enum Operation, Operation,
+    addOp = EPOLL_CTL_ADD,
+    modifyOp = EPOLL_CTL_MOD,
+    deleteOp = EPOLL_CTL_DEL
+}
+
+instance Show Operation where
+    show op | op == addOp    = "EPOLL_CTL_ADD"
+            | op == modifyOp = "EPOLL_CTL_MOD"
+            | op == deleteOp = "EPOLL_CTL_DEL"
+            | otherwise = show $ fromOp op
+
+-- | Creates an epoll device. Must be closed with 'close'.
+-- The parameter 'Size' specifies the number of events that can be reported by a
+-- single call to 'wait'.
+create :: Size -> IO Device
+create s = do
+    dev <- throwErrnoIfMinus1 "create: c_epoll_create" (c_epoll_create 0)
+    buf <- mallocArray (fromIntegral $ fromSize s)
+    installHandler sigPIPE Ignore Nothing
+    return $ Device (Fd dev) buf s
+
+-- | Closes epoll device.
+close :: Device -> IO ()
+close = free . eventArray
+
+-- | Adds a filedescriptor to the epoll watch set using the specified
+-- EventTypes. User data might be passed in as well which will be returned on
+-- event occurence as part of the 'Event' type. Returns an event descriptor
+-- which must be deleted from the watch set with 'delete'.
+add :: Device -> a -> [EventType] -> Fd -> IO (Descriptor a)
+add dev dat evts fd = do
+    p <- newStablePtr (fd, dat)
+    control dev addOp evts p
+    return $ Descriptor p
+
+-- | Modified the event types of the event descriptor.
+modify :: Device -> [EventType] -> Descriptor a -> IO ()
+modify dev evts des = control dev modifyOp evts (descrPtr des)
+
+-- | Removes the event descriptor from the epoll watch set.
+delete :: Device -> Descriptor a -> IO ()
+delete dev des = do
+    control dev deleteOp [] (descrPtr des)
+    freeStablePtr (descrPtr des)
+
+-- | Representation of epoll_ctl.
+control :: Device -> Operation -> [EventType] -> StablePtr (Fd, a) -> IO ()
+control dev op evts ptr = do
+    (fd, _) <- deRefStablePtr ptr
+    throwErrnoIfMinus1_ ("control: c_epoll_ctl (" ++ (show op) ++ ")")
+        (with (EventStruct (fromIntegral . fromEventType $ combEvents evts)
+                           (castStablePtrToPtr ptr))
+            (c_epoll_ctl (intToNum $ deviceFd dev)
+                         (fromIntegral $ fromOp op)
+                         (intToNum fd)))
+
+-- | Waits for the specified duration on all event descriptors. Returns the
+-- list of events that occured.
+wait :: Duration -> Device -> IO [Event a]
+wait timeout dev = do
+    r <- throwErrnoIfMinus1Retry "wait: c_epoll_wait"
+         (c_epoll_wait (intToNum $ deviceFd dev)
+                       (eventArray dev)
+                       (fromIntegral . fromSize $ eventArrayLen dev)
+                       (fromIntegral $ fromDuration timeout))
+    evts <- peekArray (fromIntegral r) (eventArray dev)
+    mapM createEvent evts
+ where
+     createEvent e = do
+        let ptr = castPtrToStablePtr $ epollData e
+            ety = EventType (fromIntegral $ epollEvents e)
+        (fd, ref) <- deRefStablePtr ptr
+        return (Event fd ety ref (Descriptor ptr))
+
+-- | Match operator. Useful to test whether an 'EventType' returned from
+-- 'wait' contains one of the defined event types because EventTypes returned by
+-- wait might be the bitwise OR of several EventTypes.
+(=~) :: EventType -> EventType -> Bool
+e1 =~ e2 = fromEventType e1 .&. fromEventType e2 /= 0
+
+infix 4 =~
+
+toSize :: Int -> Maybe Size
+toSize i = toWord32 i >>= Just . Size
+
+toDuration :: Int -> Maybe Duration
+toDuration i = toWord32 i >>= Just . Duration
+
+combEvents :: [EventType] -> EventType
+combEvents = EventType . foldr ((.|.) . fromEventType) 0
+
+-- Foreign Imports --
+
+foreign import ccall unsafe "epoll.h epoll_create"
+    c_epoll_create :: CInt -> IO CInt
+
+foreign import ccall unsafe "epoll.h epoll_ctl"
+    c_epoll_ctl :: CInt -> CInt -> CInt -> Ptr EventStruct -> IO CInt
+
+foreign import ccall safe "epoll.h epoll_wait"
+    c_epoll_wait :: CInt -> Ptr EventStruct -> CInt -> CInt -> IO CInt
+
diff --git a/src/System/Linux/EpollM.hs b/src/System/Linux/EpollM.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Linux/EpollM.hs
@@ -0,0 +1,154 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+-- |
+-- Module      : System.Linux.EpollM
+-- Copyright   : (c) 2009 Toralf Wittner
+-- License     : LGPL
+-- Maintainer  : toralf.wittner@gmail.com
+-- Stability   : experimental
+-- Portability : non-portable
+--
+-- Monadic epoll interface. Similar to System.Linux.Epoll.Base but uses
+-- 'ReaderT' to hold a 'Device' instead of passing it to many functions.
+
+module System.Linux.EpollM (
+
+    Epoll,
+    runEpoll,
+    runEpoll_,
+    runEpollSmall_,
+    runEpollMed_,
+    runEpollBig_,
+    add,
+    add_,
+    modify,
+    delete,
+    wait,
+    wait_,
+    device,
+    dispatchLoop,
+    dispatchLoop_,
+    defaultDispatchLoop,
+    defaultDispatchLoop_,
+    fork,
+    fork_,
+
+    E.EventType,
+    E.Size,
+    E.toSize,
+    E.Duration,
+    E.toDuration,
+    E.Descriptor,
+    E.Device,
+    E.Event (eventFd, eventType, eventRef, eventDesc),
+    (E.=~),
+
+    E.create,
+    E.close,
+
+    E.inEvent,
+    E.outEvent,
+    E.peerCloseEvent,
+    E.urgentEvent,
+    E.errorEvent,
+    E.hangupEvent,
+    E.edgeTriggeredEvent,
+    E.oneShotEvent
+
+) where
+
+import Data.Maybe (fromJust)
+import Control.Concurrent
+import Control.Monad.Reader
+import Control.Exception (bracket)
+import System.Posix.Types (Fd)
+import qualified System.Linux.Epoll as E
+
+newtype Epoll a = Epoll {
+        runDev :: ReaderT E.Device IO a
+    } deriving (Monad, MonadIO, MonadReader E.Device) 
+
+-- | Run Epoll monad.
+runEpoll :: E.Device -> Epoll a -> IO a
+runEpoll d e = runReaderT (runDev e) d
+
+-- | Run Epoll monad. Like 'runEpoll' but creates and closes
+-- 'Device' implcitely.
+runEpoll_ :: E.Size -> Epoll a -> IO a
+runEpoll_ s e = bracket (E.create s) E.close (flip runEpoll e)
+
+-- | Like 'runEpoll_' but with an implicit 'Size' value of 8.
+runEpollSmall_ :: Epoll a -> IO a
+runEpollSmall_ = runEpollN_ 8
+
+-- | Like 'runEpoll_' but with an implicit 'Size' value of 256.
+runEpollMed_ :: Epoll a -> IO a
+runEpollMed_ = runEpollN_ 256
+
+-- | Like 'runEpoll_' but with an implicit 'Size' value of 8192.
+runEpollBig_ :: Epoll a -> IO a
+runEpollBig_ = runEpollN_ 8192
+
+-- | Adds the given file descriptor with the specified event types to epoll.
+add :: a -> [E.EventType] -> Fd -> Epoll (E.Descriptor a)
+add d e f = do
+    dev <- ask
+    des <- liftIO $ E.add dev d e f
+    return des
+
+-- | Like 'add' but without accepting custom data.
+add_ :: [E.EventType] -> Fd -> Epoll (E.Descriptor ())
+add_ = add () 
+
+-- | Modify the event type set of the given descriptor.
+modify :: [E.EventType] -> E.Descriptor a -> Epoll ()
+modify e d = do
+    dev <- ask
+    liftIO $ E.modify dev e d
+
+-- | Deletes the descriptor from epoll.
+delete :: E.Descriptor a -> Epoll ()
+delete d = do
+    dev <- ask
+    liftIO $ E.delete dev d
+
+-- | Waits up to the given duration for events on all descriptors.
+wait :: E.Duration -> Epoll [E.Event a]
+wait d = do
+    dev <- ask
+    liftIO $ E.wait d dev
+
+-- | Like 'wait' but uses a 'Duration' of 500ms.
+wait_ :: Epoll [E.Event a]
+wait_ = wait (fromJust $ E.toDuration 500)
+
+runEpollN_ :: Int -> Epoll a -> IO a
+runEpollN_ = runEpoll_ . fromJust . E.toSize
+
+device :: Epoll E.Device
+device = ask
+
+-- | Waits for events and calls the given function for each.
+dispatchLoop :: E.Duration -> (E.Event a -> Epoll ()) -> Epoll ()
+dispatchLoop dur f = forever $ wait dur >>= mapM_ f
+
+-- | Like 'dispatchLoop' but with predefined 'Duration' of 500ms.
+defaultDispatchLoop :: (E.Event a -> Epoll ()) -> Epoll ()
+defaultDispatchLoop = dispatchLoop (fromJust $ E.toDuration 500)
+
+-- | Like 'dispatchLoop' but forks itself into another thread
+dispatchLoop_ :: E.Duration -> (E.Event a -> Epoll ()) -> Epoll ThreadId
+dispatchLoop_ dur f = fork $ forever $ wait dur >>= mapM_ f
+
+-- | Like 'defaultDispatchLoop' but forks itself into another thread
+defaultDispatchLoop_ :: (E.Event a -> Epoll ()) -> Epoll ThreadId
+defaultDispatchLoop_ = dispatchLoop_ (fromJust $ E.toDuration 500)
+
+-- | Uses 'forkIO' to spark an epoll computation into another thread.
+fork :: Epoll () -> Epoll ThreadId
+fork (Epoll r) = Epoll $ mapReaderT forkIO r
+
+-- | Like 'fork' but swallows the ThreadId.
+fork_ :: Epoll () -> Epoll ()
+fork_ e = fork e >> return ()
+
diff --git a/src/Util.hs b/src/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/Util.hs
@@ -0,0 +1,15 @@
+module Util (intToNum, toWord32, forkIO_) where
+
+import Data.Word
+import Control.Concurrent (forkIO)
+
+intToNum :: (Integral a, Num b) => a -> b
+intToNum = fromIntegral . toInteger
+
+toWord32 :: Int -> Maybe Word32
+toWord32 i | i < 0     = Nothing
+           | otherwise = Just (fromIntegral i)
+
+forkIO_ :: IO () -> IO ()
+forkIO_ act = forkIO act >> return ()
+
