container for different types, avoiding boiler plate

Derek Elkins ddarius@hotpop.com
Wed, 20 Aug 2003 15:26:54 -0400


On Wed, 20 Aug 2003 08:25:39 -0700
"Hal Daume" <t-hald@microsoft.com> wrote:

> I use my 'DynamicMap' type to handle this sort of thing.  However, I
> don't really recommend this approach unless you're very careful.  You
> basically lose out on all nice type checking properties and enter a
> world of dynamic typing (more or less).
> 
> Anyway, you can find it at:
> 
>  http://www.isi.edu/~hdaume/DynamicMap.hs
> 
> it uses "NLP.FiniteMap", but you can replace this with
> "Data.FiniteMap".
> 
> You can then do things like:
> 
>   data Gender = Masc | Fem | Neutr      deriving Typeable
>   data Number = First | Second | Third  deriving Typeable
> 
>   let dm = addToDM (addToDM emptyDM Masc) Second
> 
>   case lookupDM dm of
>     Just Masc -> "is a guy"
>     Just _    -> "is not a guy"
>     _         -> "i don't know gender"
> 
>   case lookupDM dm of
>     Just First  -> "is first"
>     Just Second -> "is second"
>     _           -> "either i don't know or is third"
> 
> of course 'deriving Typeable' means you need GHC6; otherwise you can
> write the instances by hand.

One useful thing I've found when using Dynamics is that pattern guards
can be used as a relatively compact typecase.  E.g.

foo dyn | Just b <- fromDynamic dyn             = if b then "T" else "F"
        | Just (s :: String) <- fromDynamic dyn = show s
        | otherwise                             = show dyn

The only other way I could think of getting typecase like behavior at
another time before I knew about pattern guards was through a horrendous
set of nested case statements.

Another trick for debugging is it is fairly easy to get Haskell to know
the "expected" type, so that you can have error messages, in a generic
error handling routine, like, "expected Int but got <<FooBar>>".