unsafePerformIO :: IO a -> a
This is the "back door" into the IO monad, allowing IO
computation to be performed at any time. For this to be safe, the IO
computation should be free of side effects and independent of its environment.
getCurrentTime
is dependent on its environment, so unsafePerformIO
is not the way to go. However, given a MonadIO
, we can use liftIO
in order to lift the action into the appropriate monad. Lets have a look at the types to find out where we can plug it in:
-- http://hackage.haskell.org/package/happstack-server-7.3.9/docs/Happstack-Server-SimpleHTTP.html
simpleHTTP :: ToMessage a => Conf -> ServerPartT IO a -> IO ()
ServerPartT
is an instance of MonadIO
, so we could definitely plug it in here. Lets check ok
:
ok :: FilterMonad Response m => a -> m a
-- nope: ^^^
So we really need to get the current time before we prepare the response. After all, this makes sense: when you create the response, all heavy work has been done, you know what response code you can use and you don't need to check whether the file or entry in the database exists. After all, you were going to sent an 200 OK
, right?
This leaves us with the following solution:
{-# 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)
Lessons learned
- Don't use
unsafePerformIO
.
- Don't use
unsafePerformIO
, unless you're really sure what you're actually doing.
- Use
liftIO
if you want to use an IO
action in an instance of MonadIO
.