用haskell 十分钟写一个wiki程序

好吧,我承认,标题是唬人的。用十分钟实现的这个小wiki还不具备全文搜索,智能推荐,启发式屏蔽关键词等等能力。

这几天用晚上的时间研究了下haskell的web应用,见前两天的 haskell + fastcgi 篇。所应用的就是基本的 fastcgi, dbm以及 XHtml 这几个模块。 从无到有自己实现 session以及url映射等基础组件,总共的功能代码不到100行吧。有了这个基本架子之后,在上面添加一个 wiki 功能,真的只用了十多(n)分钟。(Turbogears那个视频也很唬滥,他可是在一大堆现有的模块之上搞的,我好歹是从cgi搞起)

通过几天的实践,几点感受:
1. haskell 是门简单的语言,但很多入门读物都是具有医生头衔的人写的,上来就是monad,范畴论,组合子,要么就是写个解释器之类的,真的会吓到我这样的小白
2. haskell的类型确实是个好东西,类型检查可以在编译期间排除掉大部分的错误,所以基本上,只要程序能通过编译,不大需要调试的,因为类型检查强迫你按正确的用法使用各个模块
3. dbm 是sql hater的救星

代码片段1: (url 分发部分) 如果url 不在map里,就去wiki的 dbm 里面找,找不到的话,给一个创建页面,找到了的话,就现实这个页面。

  target <- liftIO $ HT.lookup mapping uriPath
  case target of
    Nothing -> do
      -- missing page, find wiki
      let wiki = dbmWiki env
      wikiContent <- liftIO $ findWiki wiki uriPath 
      case wikiContent of
        Nothing -> do
                output $ showHtml $ pageNotFound uriPath
        Just content -> do
                output $ showHtml $ readWikiPage uriPath content

而 Wiki 模块中的几个函数,简单到不忍心拿出来的地步:

从dbm 中找一个页面是否存在: (简直就是换了个函数名而已)

findWiki dbm path = lookupA dbm path

把一个新页写入 dbm 中:

writeWikiPage dbm path content = do
  insertA dbm path content
  flushA dbm

显示wiki页面:

readWikiPage path cont = header << [
                             thetitle << path,
                             meta ! [httpequiv "Content-Type", content "text/html", strAttr "charset" "UTF-8"]
                            ] +++
                            body << pre << cont

创建页面:

pageNotFound uri = page "Page Not found" b
    where
      b = body << [
           h1 << "Page Not found",
           form ! [method "POST", action "/createPage"] << 
                    [
                     h2 << "Page Content:",
                     textarea ! [name "content", cols "100", rows "25"] << "",
                     br,
                     hidden "path" uri,
                     submit "" "Submit"
                    ]
          ]

保存页面:

createPage env sid = do
  let dbm =  dbmWiki env
  method  <- requestMethod
  path'   <- getInput "path"
  content'<- getInput "content"
  case maybe2 path' content' of
    Nothing -> do
      return (h1 << "miss field", "/")
    Just (path, content) -> do
                       liftIO $ writeWikiPage dbm path content
                       return (h1 << "write ok", path)

简陋的功能有了,现在可以访问一个不存在的url 然后显示一个创建页面,保存就ok了。 下面需要一个Index页,把所有已有的页面列出来:
下面这个函数生成一个 Html 类型的结果,内容为一个 div

wikiIndex env sid = do    
  paths <- keysA dbm             -- 把 dbm 的所有key 取出
  --  div 由一个 h1 和一系列链接组成
  return $ thediv << (h1 << "Wiki Index" +++ [ li << anchor ! [href p] << p | p <- paths])
    where
      dbm = dbmWiki env         -- 获得 wiki的dbm handle