One simple approach is to define the data type for the links, i.e.
data Link = LinkHaskell | LinkReddit deriving (Enum, Bounded) toUrl LinkHaskell = Url "http://www.haskell.org" toUrl LinkReddit = Url "http://www.reddit.org" allLinks :: [Link] allLinks = [minBound .. maxBound]
You still need to specify the name in two places, but at least now the compiler will complain if you forget to add it in one place (at least using -Wall ).
Another approach is to use Template Haskell magic:
{-# LANGUAGE TemplateHaskell #-} module Links where import Control.Monad import Language.Haskell.TH data Url = Url String deriving (Show) mkLinks :: [(String, String)] -> Q [Dec] mkLinks links = liftM2 (++) mkAllLinks $ mapM mkLink links where mkLink (name, url) = valD (varP $ mkLinkName name) (normalB [| Url url |]) [] mkAllLinks = [d| allLinks = $(listE [varE $ mkLinkName name | (name, _) <- links] )|] mkLinkName = mkName . ("link" ++)
Now you need to specify the links in only one place:
{-# LANGUAGE TemplateHaskell #-} import Links mkLinks [("Haskell", "http://www.haskell.org") ,("Reddit", "http://www.reddit.org") ,("StackOverflow", "http://www.stackoverflow.com") ] main = do putStrLn "By name:" print $ linkHaskell print $ linkReddit putStrLn "All:" mapM_ print allLinks
source share