What is the best data structure for the following scenario?
Say, you have a list of URLs
linkHaskell = Url "http://www.haskell.org"
linkReddit = Url "http://www.reddit.com"
...
and you use them individually, but you also want to operate on all of them, e.g. link-check, you could put them in a list
allLinks = [
linkHaskell
, linkReddit
...
]
But that is error-prone, since you might forget to add a new link.
You could choose to store those URLs in a Map instead, but then you would exchange compile-time errors for runtime-errors, in case you have typos in the keys.
In Haskell what would you do?
One simple approach is to define a datatype 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 have 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 with -Wall
).
Another approach is to use some 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 only have to specify the links in 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With