On Oct 27, 2005, at 11:01 AM, Joel Reymont wrote:
Folks,
With lots of help from #haskell and haskell-cafe I came up with the following setup. It's working fine but requires quite a bit of boilerplate code. Could you please help me simplify it?
I apologize for the very long message and will describe any parts that are unclear. Please ask away. This is my first Haskell code, written over the course of 3 weeks (1 week to learn Haskell) so I'm bound to get some things wrong or unoptimal. Still, I'm quite amazed that I have been able to get this to work and to work correctly in such a short time span.
Welcome to Haskell!
The system is basically a scripting engine to test a poker server that lets you write simple scripts. I went out of my way to enable QA techs to use as little Haskell as possible, thus I'm treating all poker commands/packets as a list of properties.
What I found is that I'm writing a lot of boiler-plate code to handle the convertion of property values into "storables". I think this dovetails into the recent GADT discussion. I wonder if my design and interaction between Packet, Convertible, Prop and Attr can be simplified.
[snip]
My concern is mostly with a lot of similar boilerplate code required for casting, specially in very alike cases like the following:
data Pot = Pot [Prop] deriving (Eq, Show, Typeable)
data BaseTableState = BaseTableState [Prop] deriving (Eq, Show, Typeable)
instance Packet Pot where
unstuff xs = case props
of Just props -> (Just $ Pot props, xs')
Nothing -> (Nothing, xs)
where (props, xs') = unstuffprops xs potProps <<< this is the only difference
stuff (Pot a) = stuffprops a
size (Pot a) = sizeprops a
instance Convertible [Prop] Pot where
convert_AB a = Pot $ mergeprops a potProps
convert_BA (Pot b) = b
instance Packet BaseTableState where
unstuff xs = case props
of Just props -> (Just $ BaseTableState props, xs')
Nothing -> (Nothing, xs)
where (props, xs') = unstuffprops xs baseTableStateProps
stuff (BaseTableState a) = stuffprops a
size (BaseTableState a) = sizeprops a
instance Convertible [Prop] BaseTableState where
convert_AB a = BaseTableState $ mergeprops a baseTableStateProps
convert_BA (BaseTableState b) = b
Notice that the differences are only in the list of properties required for conversion. I'm wondering if this can be simplified somehow.
You could consider creating a monad for the "unstuff" part of the operation that would hide dealing with the FastString, the tupling and the case analysis on Maybe.
Your code might then look like:
class (Eq a) => Packet a where
unstuff :: Unstuff a
stuff :: a -> P.FastString
size :: a -> Int
instance Packet BaseTableState where
unstuff = unstuffprops baseTableStateProps >>= return . BaseTableState
sutff (BaseTableState a) = stuffprops a
size (BaseTableState a) = sizeprops a
where Unstuff is the type constructor for your monad.
If you end up doing a lot of instances like this, the monad could well be a win; it also gives you the opportunity to add error reporting during the parse if you want.
As a side note, I see you are doing a bunch of operations on lists of properties. If performance is an issue, you might want to consider using Data.Map or similar. If your properties lists can get big, mergeprops looks like a potential problem ( O( n*(n+m) ) each time it's called ).