Проблема.
Осмелюсь предложить ассоциированные типы.
{-# LANGUAGE TypeFamilies #-}
data ErrRes = ErrRes String
class Send a where { send :: a -> IO (); recv :: IO a }
class (Send a, Send (Res a)) => Exch a where
type Res a
-- |Обмен.
exch :: a -> IO (Either ErrRes (Res a))
-- |Примеры использования.
data KeepAlive = KeepAlive
data Ack = Ack Int
instance Send KeepAlive where ...
instance Send Ack where ...
instance Exch KeepAlive where
type Res KeepAlive = Ack
exch KeepAlive = ...
Для большей части требуемого функционала должно хватить.
Можно разбить Send на два класса. Но это уже мелочи.