0

我想在 haskell 中编写一个提供当前时间的简单网络服务器。时间应以 json 格式返回。

这是我到目前为止所拥有的:

{-# LANGUAGE DeriveDataTypeable #-}

import Happstack.Server
import Text.JSON.Generic
import Data.Time
import System.IO.Unsafe

data TimeStr = TimeStr {time :: String} deriving (Data, Typeable)

main = simpleHTTP nullConf $ ok $ toResponse $ encodeJSON (TimeStr $ show (unsafePerformIO getCurrentTime)) 

我知道unsafePerformIO应该避免这种情况,但我还找不到更好的解决方案。也许这就是问题所在?我对单子有一个非常基本的了解。

结果如下:

{"time":"2014-10-16 16:11:38.834251 UTC"}

问题是当我刷新localhost:8000时间并没有改变。是否正在进行某种记忆?

4

1 回答 1

5
unsafePerformIO :: IO a -> a

这是进入 IO monad 的“后门”,允许IO随时执行计算。为了安全起见,IO计算应该没有副作用并且独立于其环境

getCurrentTime依赖于它的环境,所以unsafePerformIO不是要走的路。但是,给定 a MonadIO,我们可以使用liftIOorder 将动作提升到适当的 monad 中。让我们看一下类型以找出我们可以将其插入的位置:

-- http://hackage.haskell.org/package/happstack-server-7.3.9/docs/Happstack-Server-SimpleHTTP.html
simpleHTTP :: ToMessage a => Conf -> ServerPartT IO a -> IO () 

ServerPartT是 的一个实例MonadIO,所以我们绝对可以在这里插入它。让我们检查一下ok

ok :: FilterMonad Response m => a -> m a 
--                       nope: ^^^

所以我们真的需要在准备响应之前获取当前时间。毕竟,这是有道理的:当您创建响应时,所有繁重的工作都已完成,您知道可以使用什么响应代码,并且您无需检查数据库中的文件或条目是否存在。毕竟,你要发送一个200 OK,对吧?

这给我们留下了以下解决方案:

{-# LANGUAGE DeriveDataTypeable #-}

import Happstack.Server
import Text.JSON.Generic
import Data.Time
import System.IO.Unsafe
import Control.Monad.IO.Class (liftIO)

data TimeStr = TimeStr {time :: String} deriving (Data, Typeable)

main = simpleHTTP nullConf $ do
           currentTime <- liftIO getCurrentTime
           ok $ toResponse $ encodeJSON (TimeStr $ show currentTime)

得到教训

  1. 不要使用unsafePerformIO.
  2. 不要使用unsafePerformIO,除非您真的确定自己在做什么。
  3. 如果liftIO您想IO在 的实例中使用操作,请使用MonadIO
于 2014-10-16T17:43:15.180 回答