简单登录Haskell

时间:2014-05-23 15:10:48

标签: haskell

我只是想设计一个基于以下两个函数的简单日志记录系统:

-- Prints before (if logging is on), does action, 
-- prints after (if logging is on), returns action
log :: b -> c -> IO a -> IO a
log before after action = ...

-- Sets logging on/off for the duration of the passed action.
setLog :: Bool -> IO a -> IO a
setLog doLog action = ...

setLog False $
  log "This doesn't get printed" "Neither does this" (putStr "But this does")

只会打印:

"But this does"

但是这个:

setLog True $
  log "This does get printed" "This does too" (putStr "And this does")

会打印:

"This does get printed"
"And this does"
"This does too"

这是我正在尝试设置的简化版本。 或者,如果有更好的方法来解决这个问题,我很高兴听到它。

2 个答案:

答案 0 :(得分:1)

您可以使用StateT monad变换器轻松完成此操作。举个简短​​的例子:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

import Control.Applicative
import Control.Monad
import Control.Monad.State

data LogState = LogOff | LogOn
    deriving (Eq, Ord, Enum, Bounded, Show, Read)

newtype LogT m a = LogT { unLogT :: StateT LogState m a}
    deriving (Functor, Monad, Applicative, MonadState LogState, MonadTrans, MonadIO)

runLog :: Monad m => LogT m a -> m a
runLog action = evalStateT (unLogT action) LogOn


loggingEnabled :: (Functor m, Monad m) => LogT m Bool
loggingEnabled = fmap (== LogOn) get

log' :: (Functor m, Monad m) => (l -> LogT m ()) -> l -> l -> m a -> LogT m a
log' logger before after action = do
    doLog <- loggingEnabled
    when doLog $ logger before
    result <- lift action
    when doLog $ logger after
    return result

-- The function `log` already exists in Prelude as the logarithm, use a different name
log_ :: (Functor m, MonadIO m) => String -> String -> m a -> LogT m a
log_ = log' (liftIO . putStrLn)

enableLogging :: Monad m => LogT m ()
enableLogging = put LogOn

disableLogging :: Monad m => LogT m ()
disableLogging = put LogOff


main :: IO ()
main = runLog $ do
    enableLogging
    log_ "You'll see this" "Then this" $
        putStrLn "First action"
    disableLogging
    log_ "But not this" "or this" $
        putStrLn "Second action"

感谢GeneralizedNewtypeDeriving,我们可以自动为新的monad转换器生成有用的实例,例如FunctorMonadMonadIOMonadTrans 。我们可以像使用StateT LogState monad一样使用它,并使用它来存储是否启用了日志记录。如果您愿意,您可以简单地扩展这个,例如,如果您想控制日志记录级别(DEBUG / INFO / WARNING / ERROR),只需更改LogState以满足您的需求。我还将log'留在那里作为通用记录器,以防您希望将其写入WriterT monad,或者写入文件或完全执行其他操作。它非常灵活。

答案 1 :(得分:0)

setLog :: Bool -> ((String -> IO ()) -> IO a) -> IO a 
setLog True = ($ putStrLn)
setLog False = ($ (const $ return ()))

log' f x y ac = f x >> ac >>= \a -> f y >> return a 

test0 = setLog False $ \f -> do
          log' f "This doesn't get printed" "Neither does this" (putStr "But this does")

test1 = setLog True $ \f -> do
          log' f "This does get printed" "This does too" (putStr "And this does")