我想用haskell编写一个简单的Web服务器,它提供当前时间。时间应该以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
时,时间不会改变。有什么记忆在进行吗?
unsafePerformIO :: IO a -> a
这是IO monad的"后门",允许随时执行
IO
计算。为了安全起见,IO
计算应该没有副作用,并且与其环境无关。
getCurrentTime
依赖于它的环境,所以unsafePerformIO
是而不是的选择。然而,给定MonadIO
,我们可以使用liftIO
将动作提升到适当的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)
经验教训
- 不要使用
unsafePerformIO
- 不要使用
unsafePerformIO
,除非您真的确定自己在做什么 - 如果要在
MonadIO
的实例中使用IO
操作,请使用liftIO