[Haskell-cafe] Haskell scripting system (please help me simplify the design)

Robert Dockins robdockins at fastmail.fm
Thu Oct 27 11:59:30 EDT 2005


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   ).

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org//pipermail/haskell-cafe/attachments/20051027/f1ebdc98/attachment.htm


More information about the Haskell-Cafe mailing list