packages feed

oneOfN-0.1: src-TH/Data/OneOfN.hs

{-# LANGUAGE TemplateHaskell, DeriveDataTypeable #-}
module Data.OneOfN where

import Data.Char (toLower)
import Data.Data (Data, Typeable)
import Language.Haskell.TH

$( do
    let minN = 2
        maxN = 20
        maxTypeableN = 7
        lc []     = []
        lc (c:cs) = toLower c : cs
        
        -- same naming scheme as HaXml, but start using numerals at 10,
        -- as per (one) standard English writing convention.
        nOfM n m = concat [numWord n, "Of", show m]
            where
                numWords = ["Zero","One","Two","Three","Four","Five",
                            "Six","Seven","Eight","Nine"]
                numWord n
                    | n < length numWords   = numWords !! n
                    | otherwise             = "Choice" ++ show n
        typeVarName n = mkName ("t" ++ show n)
        
        mkOneOfN m = do
            -- the data declaration
            ty <- dataD (cxt []) (mkName (nOfM 1 m))
                (map (PlainTV . typeVarName) [1..m]) 
                [ normalC (mkName (nOfM n m)) [strictType isStrict (varT (typeVarName n))] 
                    | n <- [1..m]]
                ([''Eq, ''Ord, ''Read, ''Show]
                    ++ if m <= maxTypeableN then [''Data, ''Typeable] else [])
            
            -- a "folding" function, like 'Prelude.either'
            let f n = mkName ("f" ++ show n)
                it  = mkName "it"
                x   = mkName "x"
            fun <- funD (mkName (lc (nOfM 1 m)))
                [ clause
                    (map (varP . f) [1..m] ++ [varP it])
                    (normalB (caseE (varE it)
                        [ match (conP (mkName (nOfM n m)) [varP x])
                            (normalB (appE (varE (f n)) (varE x))) []
                        | n <- [1..m]
                        ])) []
                ]
            
            return [ty, fun]
    
    fmap concat (mapM mkOneOfN [minN..maxN])
 )