如何登录Haskell?

non*_*ont 44 monads logging haskell

我正在尝试使用HSlogger获取有关我的程序的一些信息.所以我在我的函数中添加以下行

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."
     ...
Run Code Online (Sandbox Code Playgroud)

这很好,因为函数在IO内部.但是,当我向以下函数添加类似的行时:

...
parsePES :: Parser PESFile
parsePES = do
        header <- string "#PES"
        warningM "parsing header"
        ...
        return (PESFile ...)
Run Code Online (Sandbox Code Playgroud)

我收到类型错误:

 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";
        ...
Run Code Online (Sandbox Code Playgroud)

我完全理解为什么 - parsePES在Parser monad中,而不是IO monad.我不明白的是该怎么做.我需要一个monad变换器,所以我可以将Parser monad和IO monad堆叠在一起吗?我该怎么做?

C. *_*ann 49

首先,快速免责声明:"日志记录"在一般的Haskell代码中通常没有意义,因为它假定某种顺序执行可能有意义也可能没有意义.确保区分记录程序执行方式记录计算值.在严格的命令式语言中,这些语言基本相同,但在Haskell中则不然.

也就是说,听起来你想在已经连续和有状态的计算的上下文中基于计算的值进行记录,这几乎和大多数其他语言的记录一样.但是,你确实需要monad来支持这样做的一些方法.看起来你正在使用的解析器来自HCodecs包,它似乎相对有限,不允许IO,并且没有被定义为monad转换器.

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

最后一个选项,如果你真的想坚持你所拥有的,那就是看看你现在正在使用的解析库的实现,并推出你自己IO的版本.但这可能并不理想.


另外,作为一个附录,如果您真正关注的是实际上没有记录,而只是跟踪程序的执行作为开发的一部分,您可能会发现GHCi中内置的调试器更有帮助,或者说是老式的通过Debug.Trace模块进行 printf调试.


编辑:好的,听起来你似乎有理由考虑滚动自己的变化.你最近想要的是ParserTmonad变压器.这是目前的定义Parser:

newtype Parser a = Parser { unParser :: S -> Either String (a, S) }
Run Code Online (Sandbox Code Playgroud)

类型S是解析器状态.请注意,这大致是硬编码版本StateT S (Either String) a:

newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
Run Code Online (Sandbox Code Playgroud)

...... Either String被视为错误monad的地方.该ErrorT单子转换做同样的事情:

newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }
Run Code Online (Sandbox Code Playgroud)

那么当前类型相当于StateT S (ErrorT String Identity),你想要的是什么StateT S (ErrorT String IO).

看起来模块中的大多数函数都没有弄乱Parsermonad 的内部,所以你应该能够简单地替换类型定义,提供适当的类型类实例,编写自己的runParser函数,并且好好去.

  • 我认为你关于Debug.Trace的附录就是我正在构建我的模块时我真正想要的.跟踪可以解决问题.谢谢!我很高兴你的整个解释,因为通用日志记录也会有所帮助,而且我不知道该怎么做. (2认同)

Woj*_*ilo 26

免责声明:我是Logger haskell框架的作者.

尽管McCann的答案非常详细,但并未说明Haskell在提出问题时缺乏通用的日志记录框架.HSLogger现在是一个标准,但它提供了非常基本的日志记录功能,同时速度慢且不可扩展.需要说明的是,以下是HSLogger的一些缺陷:

  1. 这很慢.我的意思是,每当你记录一条消息时,它就会解析(以非常简单的方式)一个描述日志来源的字符串,并使用一些存在的数据类型,这必须在运行时引入一些性能开销.
  2. 它不允许登录其他monad而不是IO,所以你必须使用WriterT或其他解决方案不要弄乱你的代码.
  3. 它不可扩展 - 您无法创建自己的优先级,定义自定义行为(如线程间日志记录)或编译时间日志过滤.
  4. 它不提供某些信息,例如放置日志的行号或文件名.当然,扩展它以支持这些信息非常困难.

话虽如此,我很乐意介绍Logger haskell框架.它允许有效和可扩展的日志记录,包括:

  1. 登录顺序纯代码(执行以及使用WriterTmonad)
  2. 高级消息过滤(包括编译时过滤)
  3. 线程间日志记录能力
  4. 提供TemplateHaskell允许记录其他详细信息的接口,例如文件编号或模块名称
  5. 非常容易扩展 - 所有功能都是作为一个简单的扩展而创建的BaseLogger,它无法做任何明智的事情.需要明确的是 - 过滤功能在不到20行中创建,作为记录变压器,您可以定义自己的变压器.文档中描述了如何操作.
  6. 默认情况下在所有平台上提供彩色输出.

但是这个库很新,所以它缺少一些必要的功能.好的信息是,您可以自己轻松创建此功能,或通过在GitHub上报告请求来帮助我们改进它.

记录器由我正在工作的公司(luna-lang.org)内部开发,并在我们正在创建的编译器中使用.

  • 别担心!在这种情况下,我将只执行我自己的建议。作为一名新的Haskeller,我一直在考虑为使用的软件包添加/修复文档,以便以较小的方式回馈。当然,我必须确保我理解该程序包,尽管仅添加示例,但我猜总是可行的:-) (2认同)