可选参数以及 case 语句

Optional parameters along with a case statement

我了解了 hslogger here

initLogging :: Priority -> IO ()
initLogging level = do
    stdOutHandler <- streamHandler stdout level >>= \lh -> return $
            setFormatter lh (simpleLogFormatter "[$prio:$loggername:$time] $msg")
    updateGlobalLogger rootLoggerName (setLevel level . setHandlers [stdOutHandler])

我想制作一个 "quicklog" 函数,只需要较少的输入即可用于调试:如果 logtype 指定为 "I",将使用 infoM,如果指定作为 "D",将使用 debugM 等,如果未指定日志级别,则默认为 INFO

我可以制作一个满足我需要的版本:

qLog :: String -> String -> IO ()
qLog l msg = log' "" msg where
  log' "I" msg = infoM "" msg
  log' "D" msg = debugM "" msg
  log' "E" msg = errorM "" msg
  log' "W" msg = warningM "" msg
  log' _ msg = infoM "" msg

这有效:

λ> qlog "D" "Thing!"
[DEBUG::2017-01-18 20:38:19 EST] Thing!

λ> qlog "I" "Thing!"
[INFO::2017-01-18 20:38:19 EST] Thing!

但如果没有指定级别,我无法选择 infoM,a la:

λ> qlog "Thing!"
[INFO::2017-01-18 20:38:19 EST] Thing!

这完全失败了,期望 "Thing!" 是可选的日志级别,而不是 msg 参数。我相信这需要一个 Maybe 来传达第一个参数可能根本不存在,但似乎无法正确处理。这是我的尝试:

qLog2 :: Maybe String -> String -> IO ()
qLog2 l msg = case l msg of
     (Just "D") msg -> debugM "" msg
     (Just "I") msg -> infoM "" msg
     Nothing  msg -> infoM "" msg

qLog3 :: Maybe String -> String -> IO ()
qLog3 l msg 
  | Just "D" msg = debugM "" msg
  | Just "I" msg = infoM "" msg
  | Nothing  msg = infoM "" msg

我似乎无法在类型声明中充分表达 how to maybe + caseguard 的组合。

更新 1

qLog4 "D" msg = debugM "" msg
qLog4 "I" msg = infoM "" msg
qLog4 msg  = infoM "" msg

给我:

Equations for ‘qLog4’ have different numbers of arguments

更新 2:

在我通过 here

找出可选参数部分时放弃案例
import Data.Optional

newtype LogLevel = LogLevel { getLogLevel :: Char } 

qLog' :: Char -> String -> IO ()
qLog' "I" msg = infoM "" msg
qLog' "D" msg = debugM "" msg
qLog' "W" msg = warningM "" msg
qLog' "E" msg = errorM "" msg
qLog' _ msg = infoM "" msg

qLog5 :: (Optional LogLevel) -> String -> IO ()
qLog5 (Specific l) msg = qLog' (getLogLevel l) msg 
qLog5 (Default) msg = qLog' "I" msg


<interactive>:251:7-9: error:
    • Couldn't match expected type ‘Char’ with actual type ‘[Char]’
    • In the pattern: "

qLog2 的固定版本是

qLog2 :: Maybe String -> String -> IO ()
qLog2 l msg =
  case l of
    Just "D" -> debugM "" msg
    Just "I" -> infoM "" msg
    Nothing  -> infoM "" msg

如果您要重载 qlog 以获取您需要使用类型类的关卡的可选参数。这个功能用起来会很烦人。如果您希望它具有 String -> IO ()IO () 类型,那么当您编写 qlog "I" 时会产生歧义。也许您为信息级别创建了一个日志函数,或者您正在记录消息 "I"

只需要对l参数进行案例分析:

qLog :: Maybe String -> String -> IO ()
qLog l msg = case l of
    Just "D" -> debugM "" msg
    Just "I" -> infoM "" msg
    -- etc.
    Nothing  -> infoM "" msg

(如果您实际上还需要对 msg 进行案例分析,您可以通过元组来完成 - case (l, msg) of - 正如 Alec 建议的那样,或者只需将 case 语句替换为多个方程。)


不相关的建议:除了字符串,您可以使用自定义类型来表示您的日志级别。这既让事情变得更清晰,也消除了将 "G""foobar" 作为日志级别传递的风险。

-- Feel free to use less terse names.
data LogLevel = D | I | W | E
    deriving (Eq, Show)

qLog :: Maybe LogLevel -> String -> IO ()
qLog l msg = case l of
    Just D -> debugM "" msg
    Just I -> infoM "" msg
    Just W -> warningM "" msg
    Just E -> errorM "" msg
    Nothing -> infoM "" msg

您还可以分解出大部分重复性:

-- I added a few superflous parentheses to emphasise what is going on.
qLog :: Maybe LogLevel -> String -> IO ()
qLog ml msg = case ml of
     Just l -> (fLog l) "" msg
     Nothing -> infoM "" msg
     where
     fLog :: LogLevel -> (String -> String -> IO ())
     fLog l = case l of
         D -> debugM
         I -> infoM
         W -> warningM
         E -> errorM

或者,使用 maybe 巧妙地消除 maybe-ness:

GHCi> :t maybe
maybe :: b -> (a -> b) -> Maybe a -> b
-- There are also some superflous parentheses here.
qLog :: Maybe LogLevel -> String -> IO ()
qLog ml msg = (maybe infoM fLog ml) "" msg
     where
     fLog :: LogLevel -> (String -> String -> IO ())
     fLog l = case l of
         D -> debugM
         I -> infoM
         W -> warningM
         E -> errorM

如果我理解你的问题是正确的,那么你可以这样做:

{-# LANGUAGE GADTs #-}

qlog :: LogResult a => a
qlog = log' []

class LogResult a where
    log' :: [LogArg] -> a

class IsLogArgument a where
    toLogArg :: a -> LogArg

data LogArg = LogLevel LogLevel
            | LogMessage String

data LogLevel = D | I | W | E

instance IsLogArgument LogLevel where
    toLogArg = LogLevel

instance (a ~ Char) => IsLogArgument [a] where
    toLogArg = LogMessage

instance (IsLogArgument arg, LogResult result) => LogResult (arg -> result) where
    log' args arg = log' ((toLogArg arg) : args)

instance (a ~ ()) => LogResult (IO a) where
    log' args = tell "" msg
      where
        tell = case level of
                D -> debugM
                I -> infoM
                W -> warningM
                E -> errorM
        level = fromMaybe I $ look logLevel
        msg   = fromMaybe "No message" $ look logMessage

        logLevel (LogLevel x) = Just x
        logLevel _            = Nothing

        logMessage (LogMessage x) = Just x
        logMessage _              = Nothing

        look = listToMaybe . catMaybes . flip map args

并且:

λ> qlog D "Thing!"
[DEBUG::2017-01-18 20:38:19 EST] Thing!

λ> qlog "Thing!"
[INFO::2017-01-18 20:38:19 EST] Thing!