我如何在Haskell中进行日志记录? [英] How do I do logging in Haskell?

查看:101
本文介绍了我如何在Haskell中进行日志记录?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图使用HSlogger获取有关我的程序的一些信息。所以我在我的函数中添加了以下行:

$ p $ import $ Data.Word
将限定的Data.ByteString导入为B
将合格的Data.ByteString.Lazy导入为L
导入Data.Bits
导入Data.Int
导入Data.ByteString.Parser

导入System.Log。 Logger
import System.Log.Handler.Syslog


importFile :: FilePath - > IO(或者String(PESFile))
importFile n = do
warningMMyApp.Component2Something Bad is coming to happen。
...

而且工作正常,因为函数在IO内部。
然而,当我添加一个类似的行到以下函数:

  ... 
parsePES ::解析器PESFile
parsePES =执行
头文件< - 字符串#PES
warningM解析头文件
...
return(PESFile ...)

我得到一个类型错误:

 无法匹配预期类型`Parser a0'
,其实际类型为`String - > IO()'
在'warningM'调用的返回类型中
在'do'表达式的语句中:warningM解析头文件
在表达式中:
做{header< - string#PES;
warningM解析标题;
...

我完全明白为什么 - parsePES在解析器monad中,而不是IO monad。
我不明白的是该怎么做。我需要一个monad变压器吗,这样我可以将解析器monad和IO monad堆叠在一起吗?首先,快速免责声明:日志记录通常在Haskell中通常不会有意义代码,因为它假定某种顺序执行可能有意义,也可能没有意义。确保区分记录程序执行的方式记录计算的值。在严格的命令式语言中,这些大部分是相同的,但在Haskell中它们不是。

这就好像你想根据正在计算的值进行记录,已经是顺序且有状态的计算的上下文,这与大多数其他语言的登录几乎一样。但是,您确实需要monad来支持这样做的一些手段。看起来您正在使用的解析器是来自HCodecs包的 ,这似乎是相对有限的,不允许 IO ,并且未被定义为monad变换器。



老实说,我的建议是考虑使用不同的解析库。 Parsec 往往是一种默认选择,我认为 attoparsec 很受特定用途的欢迎(可能包括您正在做的事情)。您可以更容易地添加日志记录:Parsec是一个monad转换器,因此您可以将它放在 IO 之上,然后使用 liftIO ,而attoparsec是围绕增量处理设计的,所以你可以将你的输入和日志方面分块处理(尽管在实际分析器中进行日志记录可能会更加笨拙)。还有其他的选择,但我不知道足够的细节来提出建议。大多数基于解析器组合器的库往往具有相似的设计,所以我希望移植代码很简单。



最后一个选项,如果你真的想要坚持你所拥有的东西,那就是看看你现在使用的解析库的实现,并将你自己的 IO >为导向它的版本。但这可能并不理想。




另外,如果你真的不是真正的追求,日志记录,但只是将程序的执行作为开发的一部分进行追踪,您可能会发现GHCi中内置的调试器更加有用,或者通过 Debug.Trace模块




编辑:好的,听起来您似乎有理由考虑滚动自己的变体。你大致想要的是一个 ParserT monad变换器。这是当前定义 Parser

  newtype Parser a = Parser { unParser :: S  - >字符串(a,S)} 

S 是解析器状态。请注意,这大致是 StateT S(任一字符串)a 的硬编码版本:

  newtype StateT sma = StateT {runStateT :: s  - > m(a,s)} 

...其中 code>正被视为错误单子。 ErrorT monad转换器可以做同样的事情:

  newtype ErrorT ema = ErrorT {runErrorT :: m(ea)} 

所以当前类型相当于 StateT S(ErrorT String Identity),你想要的是 StateT S(ErrorT String IO)



它看起来像模块中的大部分函数不会干扰 Parser monad的内部,所以您应该能够简单地替换类型定义,提供适当的类型实例,编写自己的 runParser 函数,并且可以继续。


I'm attempting to use HSlogger to get some information about my program. So I add the following line to my function

import Data.Word
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Data.Bits
import Data.Int
import Data.ByteString.Parser

import System.Log.Logger
import System.Log.Handler.Syslog


importFile :: FilePath -> IO (Either String (PESFile ))
importFile n = do
     warningM "MyApp.Component2" "Something Bad is about to happen."
     ...

And that works fine, because the function is inside IO. However when I add a similar line to the following function:

...
parsePES :: Parser PESFile
parsePES = do
        header <- string "#PES"
        warningM "parsing header"
        ...
        return (PESFile ...)

I get a type error:

 Couldn't match expected type `Parser a0'
                with actual type `String -> IO ()'
    In the return type of a call of `warningM'
    In a stmt of a 'do' expression: warningM "parsing header"
    In the expression:
      do { header <- string "#PES";
           warningM "parsing header";
        ...

And I totally understand why - parsePES is in the Parser monad, not the IO monad. What I don't understand is what to do about it. Do I need a monad transformer so I can stack the Parser monad and the IO monad together? How do I go about that?

解决方案

First, a quick disclaimer: "logging" doesn't usually make sense in general Haskell code, because it assumes some sort of sequential execution that may or may not be meaningful. Make sure you distinguish between logging how the program executes and logging what values are computed. In strict imperative languages these are mostly the same, but in Haskell they aren't.

That said, it sounds like you want to log based on values being computed, in the context of an already sequential and stateful computation, which pretty much works the same as logging in most other languages does. However, you do need the monad to support some means of doing so. It looks like the parser you're using is from the HCodecs package, which seems to be relatively limited, doesn't allow IO, and isn't defined as a monad transformer.

Honestly my advice would be to consider using a different parsing library. Parsec tends to be kind of the default choice, and I think attoparsec is popular for specific purposes (which might include what you're doing). Either would let you add logging much more easily: Parsec is a monad transformer, so you can put it on top of IO and then use liftIO as needed, whereas attoparsec is designed around incremental processing, so you can chunk your input and log aspects of the processing (though logging inside the actual parser may be more awkward). There are other choices as well but I don't know enough of the details to make a recommendation. Most parser combinator-based libraries tend to have fairly similar designs, so I'd expect porting your code would be straightforward.

A final option, if you really want to stick to what you've got, would be to look at the implementation of the parsing library you're using now and roll your own IO-oriented version of it. But that's probably not ideal.


Also, as an addendum, if you what you're really after isn't actually logging but just tracing the execution of your program as part of development, you might find the debugger built into GHCi to be more helpful, or good old-fashioned printf debugging via the Debug.Trace module.


Edit: Okay, sounds like you have plausible reasons to consider rolling your own variation. What you roughly want here is a ParserT monad transformer. Here's the current definition of Parser:

newtype Parser a = Parser { unParser :: S -> Either String (a, S) }

The type S is the parser state. Note that this is roughly a hard-coded version of StateT S (Either String) a:

newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }

...where Either String is being treated as an error monad. The ErrorT monad transformer does the same thing:

newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }

So where the current type is equivalent to StateT S (ErrorT String Identity), what you want would be StateT S (ErrorT String IO).

It looks like most of the functions in the module aren't messing with the internals of the Parser monad, so you should be able to simply replace the type definitions, supply the appropriate type class instances, write your own runParser function, and be good to go.

这篇关于我如何在Haskell中进行日志记录?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆