我想定义一个记录器功能,比如
myPutStrLn = putStrLn . (++) "log: "
main = do myPutStrLn "hello"
哪个好。现在我想用printf
格式化提供的String,就像这个
myPutStrLn $ printf "test %d" (23 :: Int)
大!由于我经常使用这种模式,因此我想将printf
分解为记录器函数:
myPrintf = logger . printf
where
-- note, this is just an example. should be
-- replaceable with any function with this
-- typesignature
logger :: String -> IO ()
logger = putStrLn . (++) "log: "
main = myPrintf "test %d" (23 :: Int)
不幸的是,这失败了
The function `myPrintf' is applied to two arguments,
but its type `String -> IO ()' has only one
In a stmt of a 'do' block: myPrintf "test %d" (23 :: Int)
In the expression: do { myPrintf "test %d" (23 :: Int) }
In an equation for `main':
main = do { myPrintf "test %d" (23 :: Int) }
GHC推断myPrintf :: String -> IO ()
,所以显然有些不对劲。我找到了关于Polyvariadic composition的内容,但我无法将其应用于我的问题。我甚至不确定它是否能解决我的问题。
该代码也可通过gist获得。
答案 0 :(得分:1)
您可以使用hPrintf和标准输出句柄来定义您的功能。
就像那样,函数myPrintf的结果仍然是类HPprintfType的实例
myPrintf:: (HPrintfType c) => String -> c
myPrintf = (hPrintf stdout) . (printf "log:%s")
main = myPrintf "test %d" (23 :: Int)
printf函数的polyvariadic形式仅适用于您具有此实例定义:
(PrintfArg a, PrintfType r) => PrintfType (a -> r).
在每个新的PrintfArg参数中,如果可能,类型推断返回PrintfType类类型。
对于工作,您的记录器功能将具有以下类型:
logger :: (PrintfType c) => String -> c
但编译器会失败,因为函数返回IO()而不是更通用的类型类PrintfType。
在我看来,只有模块Text.Printf的修改可以帮助你,因为你无法创建PrintfType的新实例,因为某些方法是隐藏的