可选参数以及 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 + case
或 guard
的组合。
更新 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!
我了解了 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 + case
或 guard
的组合。
更新 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!