列表与地图(关键安全与所有元素的映射)

Len*_*low 6 haskell data-structures

以下场景的最佳数据结构是什么?

比如,你有一个URL列表

linkHaskell = Url "http://www.haskell.org"
linkReddit = Url "http://www.reddit.com"
...
Run Code Online (Sandbox Code Playgroud)

并且你单独使用它们,但是你也希望对它们进行操作,例如链接检查,你可以把它们放在一个列表中

allLinks = [
    linkHaskell
  , linkReddit
  ...
  ]
Run Code Online (Sandbox Code Playgroud)

但这很容易出错,因为您可能忘记添加新链接.

您可以选择将这些URL存储在Map中,但是如果您在键中存在拼写错误,则会为运行时错误交换编译时错误.

在Haskell你会做什么?

ham*_*mar 5

一种简单的方法是为链接定义数据类型,即

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]
Run Code Online (Sandbox Code Playgroud)

你仍然需要在两个地方指定名称,但至少现在如果忘记将它添加到一个地方(至少有-Wall),编译器会抱怨.

另一种方法是使用一些模板Haskell魔术:

{-# 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" ++)
Run Code Online (Sandbox Code Playgroud)

现在您只需在一个地方指定链接:

{-# 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
Run Code Online (Sandbox Code Playgroud)