NewCGIはCGIモナド*1で、DBのアクセスはIOモナドの中で行われるので、もはや関数型というよりは単なる手続き型にしか見えません…
import Control.Monad.Trans import Data.Maybe import qualified Database.HDBC as DB import qualified Database.HDBC.ODBC as ODBC import IO import Network.NewCGI import Text.XHtml cgiMain :: CGI CGIResult cgiMain = do setHeader "Content-Type" "text/html; charset=utf-8" id <- readInput "id" case id of Just id -> do rows <- liftIO $ DB.handleSqlError $ process id output $ renderHtml $ page rows Nothing -> output "Invalid ID." where process :: Int -> IO [[DB.SqlValue]] process id = bracket (ODBC.connectODBC "DSN=MySQL-test;") DB.disconnect (selectId id) selectId :: Int -> DB.Connection -> IO [[DB.SqlValue]] selectId id conn = do stmt <- DB.prepare conn "select * from test where id=?" DB.execute stmt [DB.toSql id] unfoldM fetch stmt fetch stmt = DB.fetchRow stmt >>= (\row -> return $ row >>= \v -> Just (v, stmt)) page :: [[DB.SqlValue]] -> Html page rows = header << (meta ! [httpequiv "content-type", content "text/html; charset=utf-8"] +++ thetitle << "Test") +++ body << format rows where format :: [[DB.SqlValue]] -> Html format rows = table << map formatRow rows formatRow :: [DB.SqlValue] -> Html formatRow row = tr << map formatColumn row formatColumn :: DB.SqlValue -> Html formatColumn column = td << toS column toS :: DB.SqlValue -> String toS (DB.SqlString s) = s toS x = show x unfoldM :: Monad m => (b -> m (Maybe (a, b))) -> b -> m [a] unfoldM f x = do v <- f x case v of Just (a, b) -> do r <- unfoldM f b return $ a:r Nothing -> return [] main :: IO () main = runCGI (handleErrors cgiMain)
*1 CGITモナドをIOモナドと合成したモナド