X11 programming in Haskell has traditionally been done using the X11 package. Many of these bindings were written by hand and, due to their reliance on FFI, are susceptible to some threading issues. xhb, a newer package, provides low-level bindings to X11 based on the same XML as xcb.

It doesn’t seem as though much has been done with XHB yet. In fact, at the time of writing (Aug 22, 2016), exactly two packages on Hackage depend on xhb. Nevertheless, I believe it hold great promise in the future of X programming in Haskell.

I’ve been working on a set of libraries that allows for layers of abstraction to be staked on top of the low-level xhb bindings. This post will become more fleshed out as I find time to finish up and document the actual libraries, but here’s an overview.

xhb-requests

(haddock here)

Most generated functions in xhb have types similar to those below:

createWindow :: Connection -> CreateWindow -> IO ()
grabKeyboard :: Connection -> GrabKeyboard -> IO (Receipt GrabStatus)

Those requests which expect responses can wait for them as follows:

getReply :: Receipt a -> IO (Either SomeError a)

Some functions are a bit different (request parameters don’t live inside a dedicated type, but are passed directly to the function):

configureWindow :: Connection -> WINDOW -> ValueParam Word16 -> IO ()
queryTextExtents :: Connection -> FONTABLE -> [CHAR2B] -> IO (Receipt QueryTextExtentsReply)

Nevertheless, thanks to disciplined code generation, xhb has a clean and uniform code base. The (in progress) xhb-requests package wraps all of these generated request functions in two classes:

class Request a where
    requestIO :: a -> Connection -> IO ()

class RequestWithReply a b | a -> b, b -> a where
    requestWithReplyIO :: a -> Connection -> IO (IO (Either SomeError b))

The instances of this class are generated from the xhb code that is itself generated from XML, which sounds ugly, but it worked quite well. This is the foundation for xhb-monad.

xhb-monad

(haddock here)

This package defines the following class:

class Monad x => XContext x where
    request :: Request a => a -> Connection -> x ()
    requestWithReply :: RequestWithReply a b => a -> Connection -> x (x (Either SomeError b))
    awaitEvent :: Connection -> x SomeEvent

instance XContext IO where
    request = requestIO
    requestWithReply = requestWithReplyIO
    awaitEvent = waitForEvent

and the following monad transformer:

class (XContext x, Monad m) => MonadX x m | m -> x where
    liftX :: x a -> m a
    askX :: m Connection
    catchErrorX :: m a -> (SomeError -> m a) -> m a
    throwErrorX :: SomeError -> m a

newtype X m a = X { runX :: ReaderT Connection (ExceptT SomeError m) a }
    deriving (Functor, Applicative, Monad, MonadIO, Typeable)

instance XContext x => MonadX x (X x) where
    liftX = X . lift . lift
    askX = X ask
    catchErrorX m f = X $ catchError (runX m) (runX . f)
    throwErrorX = X . throwError

--- mtl stuff also
instance MonadFoo Bar where...

This abstraction allows for pure X logic. It also lets us isolate unwieldy layers of computation and bits of state in the usual mtl way. xhb-mapping-state is an example of this.

xhb-mapping-state

(haddock here)

TODO