
I'm trying to implement the AMQP framing layer. This is the bottom layer of the protocol that packs and unpacks data in lazy ByteStrings. I'm using the Binary package with its "Get" and "Put" monads to do the unpacking, and that works well. But I've run into a problem and I can't find an elegant solution. I'm wondering if someone can help. The following contains a simplified version of my current design. I've mapped the AMQP basic types to Haskell types. Sometimes this is a basic mapping, sometimes to something more complex. Strings have several AMQP representations, so I've created corresponding newtype declarations and instances. Some AMQP types can contain tagged types. For instance a "map" contains (key, tag, value) triples, where the tag is a single byte indicating the type of the value. I've defined the following type: data AmqpVariant = AmqpVarWord8 Word8 | AmqpVarWord16 Word16 | AmqpVarStr8 Str8 -- And so on for about 20 more types instance Binary AmqpVariant where put (AmqpVarWord8 v)= putWord8 0x01 >> putWord8 v put (AmqpVarWord16 v) = putWord8 0x02 >> putWord16be v put (AmqpVarStr8 v) = putWord8 0x03 >> put v -- And so on for about 20 more types get = do getter <- fmap (amqpGetTable !) getWord8 getter amqpGetTable :: Array Word8 (Get AmqpVariant) amqpGetTable = array (0,255) [ (0x01, fmap AmqpVarWord8 getWord8), (0x02, fmap AmqpVarWord16 getWord16be), (0x03, fmap AmqpVarStr8 get), -- And so on for about 20 more types ] This is a Brute Force and Ignorance solution, but it will probably work. But now I've come up against another type, which AMQP calls an "array". This has a single type tag at the start, followed by N items of that type. I'm wondering how to do this. One option would be to declare data AmqpArray = AmqpArrWord8 [Word8] | AmqpArrWord16 [Word16] -- and so on This effectively duplicates the AmqpVariant solution. It would work, but it feels horrible. I get the feeling that there ought to be a better way, possibly using classes or existential types. Any suggestions? Thanks, Paul.

On Fri, Mar 21, 2008 at 11:02 AM, Paul Johnson
This effectively duplicates the AmqpVariant solution. It would work, but it feels horrible. I get the feeling that there ought to be a better way, possibly using classes or existential types. Any suggestions?
Firstly, I would use the Get and Put monads directly, rather than making them instances of Binary. I think, if I were doing it, I would start off with something like this: class AMQPValue a where putAMQP :: a -> Put instance AMQPValue Word8 where putAMQP v = putWord8 1 >> putWord8 v ... (many instances) instance (AMQPValue a) => AMQPValue [a] where putAMQP vs = putWord8 (length vs) >> mapM_ putAMQP vs You probably want a getAMQP member of the AMQPValue class too. Also
getter <- fmap (amqpGetTable !) getWord8 getter
is just
fmap (amqpGetTable !) getWord8
Hope that helps some AGL -- Adam Langley agl@imperialviolet.org http://www.imperialviolet.org

On Fri, Mar 21, 2008 at 6:41 PM, Adam Langley
Firstly, I would use the Get and Put monads directly, rather than making them instances of Binary.
Also, while I'm at it - I believe that AMQP messages and small and delineated. In which case, the Data.Binary.Strict.Get monad from the binary-strict package might be a better match (shameless self-plug) AGL -- Adam Langley agl@imperialviolet.org http://www.imperialviolet.org
participants (2)
-
Adam Langley
-
Paul Johnson