module Main where
import Data.List
import System.Environment
import qualified System.IO as IO
import System.Exit
dataDeclaration
:: Int
-- ^ Number of type variables
-> String
dataDeclaration i =
"data " ++ sType i ++ " = "
++ constructors
++ " deriving (Eq, Ord, Read, Show, Generic"
++ (if i <= 7 then ", Typeable)" else ")")
++ "\n\n"
where
constructors = list " | " . map mkConst
$ ls
mkConst l = 'S' : show i ++ l : ' ' : l : []
ls = letters i
letter :: Int -> Char
letter i = toEnum (97 + i)
sType :: Int -> String
sType i = 'S' : show i ++ types
where
types = " " ++ intersperse ' ' ls
ls = letters i
letters :: Int -> [Char]
letters i = map letter [0..(i - 1)]
list :: [a] -> [[a]] -> [a]
list s = concat . intersperse s
partitionDef :: Int -> String
partitionDef i = sig ++ decl
where
sig = fName ++ " :: ["
++ sType i ++ "] -> " ++ tuple ++ "\n"
tuple = "(" ++ (list ", " . map lsSig $ ls)
++ ")"
ls = letters i
lsSig l = '[' : l : ']' : []
fName = "partitionS" ++ show i
decl = fName ++ " = foldr fn " ++ tup ++ "\n" ++ whr
where
tup = "(" ++ (list ", " . replicate i $ "[]")
++ ")"
whr = " where\n" ++ cse
cse = " fn it " ++ tup ++ " = case it of\n" ++ cases ++ "\n"
where
tup = "("
++ ( list ", " . map (\l -> l:'s':[])
$ ls)
++ ")"
cases = concatMap mkCase ls
mkCase l = " S" ++ show i ++ l : ' ' : l
: " -> ("
++ (list ", " . map mkLetter $ ls)
++ ")\n"
where
mkLetter ltr
| ltr == l = l : ':' : l : 's' : []
| otherwise = ltr : 's' : []
caseDef :: Int -> String
caseDef i = sig ++ decl
where
sig = fName ++ " :: " ++ fns
++ " -> " ++ sType i ++ " -> z\n"
fns = list " -> " . map mkFn $ ls
ls = letters i
mkFn l = '(' : l : " -> " ++ "z)"
fName = "caseS" ++ show i
decl = fName ++ " " ++ (list " " hofNames) ++ " " ++ sName
++ " = case " ++ sName ++ " of\n" ++ cases ++ "\n"
sName = 's' : show i
hofNames = map (\s -> 'f':s:[]) ls
cases = concatMap mkCase ls
mkCase l = " S" ++ show i ++ l : ' ' : l
: " -> " ++ ('f' : l : ' ' : l : "\n")
mapDef :: Int -> String
mapDef i = sig ++ decl
where
fName = "mapS" ++ show i
sig = fName ++ " :: " ++ list " -> "
(fns ++ sType i : resType : []) ++ "\n"
resType = 'S' : show i ++ (' ' : list " " ls')
ls = letters i
ls' = map (\l -> l : '1' : []) ls
fns = map mkFn ls
mkFn l = '(' : l : " -> " ++ l : "1)"
decl = fName ++ " " ++ list " " (map (:[]) ls) ++ " = "
++ "caseS" ++ show i ++ " " ++ (list " " . map mkMapper $ ls)
++ "\n\n"
mkMapper l = "(S" ++ show i ++ l : " . " ++ l : ")"
mapDefA :: Int -> String
mapDefA i = sig ++ decl
where
fName = "mapS" ++ show i ++ "f"
sig = fName ++ " :: "
++ "Functor ftr =>\n"
++ " " ++ list " -> "
(fns ++ sType i : resType : []) ++ "\n"
resType = "ftr (" ++ 'S' : show i ++ (' ' : list " " ls')
++ ")"
ls = letters i
ls' = map (\l -> l : '1' : []) ls
fns = map mkFn ls
mkFn l = '(' : l : " -> " ++ "ftr " ++ l : "1)"
decl = fName ++ " " ++ list " " (map (:[]) ls) ++ " = "
++ "caseS" ++ show i ++ " " ++ (list " " . map mkMapper $ ls)
++ "\n\n"
mkMapper l = "(fmap S" ++ show i ++ l : " . " ++ l : ")"
makeModule
:: String
-- ^ Module name
-> Int
-- ^ Maximum number of type variables
-> String
makeModule n i =
pragmas ++ notice ++ leadHaddocks ++ modName
++ imports ++ datas ++ parts
++ cases ++ maps ++ mapFs
where
pragmas = "{-# LANGUAGE DeriveDataTypeable, DeriveGeneric #-}\n\n"
notice = unlines
[ "-- Text of this module generated by the generate-sums.hs"
, "-- script, included with the source of the anonymous-sums"
, "-- package."
, ""
]
leadHaddocks = unlines
[ "-- | Anonymous sum types."
, "--"
, "-- Provides functionality similar to that of tuples, but"
, "-- for sum types rather than product types. Less clumsy"
, "-- than using nested 'Either'."
, ""
]
modName = "module " ++ n ++ " where\n\n"
imports = unlines
[ "import Data.Typeable"
, "import GHC.Generics hiding (S1)"
, ""
]
datas = "-- * Anonymous sum types\n\n"
++ concatMap dataDeclaration vs
parts = "-- * Partitioning\n\n"
++ concatMap partitionDef vs
cases = "-- * Case analysis\n\n"
++ concatMap caseDef vs
maps = "-- * Mapping\n\n"
++ concatMap mapDef vs
mapFs = "-- * Mapping in a Functor\n\n"
++ concatMap mapDefA vs
vs = [1..i]
usage
:: String
-- ^ Program name
-> String
usage n = unlines
[ "usage: " ++ n ++ " MODULE_NAME NUM_VARS"
, "where"
, " MODULE_NAME is the name of the module you are"
, " creating; e.g. 'Data.Sums'"
, " NUM_VARS is the maximum number of type variables"
, " you will create."
, ""
, " Output is sent to standard output."
]
main :: IO ()
main = do
as <- getArgs
pn <- getProgName
(name, nVars) <- case as of
x:y:[] -> return (x, read y)
_ -> IO.hPutStr IO.stderr (usage pn) >> exitFailure
putStr $ makeModule name nVars