我怎么在Haskell登录?

问题描述 投票:44回答:3

我试图用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."
     ...

这工作得很好,因为该功能是内部IO。然而,当我添加了类似线以下功能:

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

我得到一个类型的错误:

 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";
        ...

我完全理解为什么 - parsePES是解析器单子,而不是IO单子。我不明白的是怎么做的。我需要一个单子转换,所以我可以堆叠解析器单子和IO单子在一起吗?我怎么去的?

logging haskell monads
3个回答
50
投票

首先,快速免责声明:“日志”通常不会让一般Haskell代码的意义,因为它假定某种顺序执行,可能会或可能不会是有意义的。确保你登录程序如何执行和记录值计算什么区别。在严格的命令式语言,这些大多是相同的,但在Haskell他们不是。

这就是说,它听起来像你想登录基于价值被计算,在已经连续和有状态计算,其中相当多的工作方式相同的日志记录在大多数其他语言确实的内容。但是,你确实需要的单子来支持这样的一些手段。它看起来像您正在使用的解析器from the HCodecs package,这似乎是相对有限的,不允许IO,并没有被定义为一个单子转换。

老实说,我的建议是考虑使用不同的解析库。 Parsec往往是一种默认的选择,我认为attoparsec是流行的用于特定用途(其中可能包括你在做什么)。这些都会让你添加记录更容易:秒差距是一个单子转换,所以你可以把它放在IO的顶部,然后用liftIO根据需要,而attoparsec是围绕增量处理设计的,这样你就可以大块的输入和记录方面的处理(尽管实际解析器内部记录可能更笨拙)。还有其他的选择,以及,但我不知道足够的细节提出建议。大多数语法基于组合子的库往往有相当类似的设计,所以我希望你移植代码将是直接的。

最后一个选择,如果你真的要坚持你有什么,就看你现在正在使用的解析库的实现和推出它自己的面向IO版本。但是,这可能并不理想。


此外,作为附录,如果你的真实意图是不实际记录,但只是跟踪你的程序的执行作为发展的一部分,你可能会发现内置GHCI调试器来提供更多的帮助,或老式printf的调试通过the Debug.Trace module


编辑:好吧,听起来像是你有合理的理由来考虑滚动自己的变化。你大概想在这里什么是ParserT单子转换。下面是Parser当前定义的:

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

类型S是解析器状态。请注意,这是粗略StateT S (Either String) a的硬编码的版本:

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

...其中Either String被视为错误单子。该ErrorT单子转换做同样的事情:

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

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

它看起来像大多数的模块中的功能不与Parser单子的内部搞乱,所以你应该能够只需更换类型定义,提供相应类型的类实例,写自己的runParser功能,以及良好去。


25
投票

免责声明:我是Logger haskell framework的作者。

尽管麦肯的回答是大大的详细,它并没有告诉,这哈斯克尔是缺乏的问题被问的时候通用日志框架。该HSLogger是一个标准的现在,但它同时是缓慢的,而不是扩展提供了非常基本的日志记录功能。需要明确的是,这里有HSLogger的一些缺陷:

  1. 它是缓慢的。由于是慢我的意思是,每次你登录它解析(在一个非常简单的方法)描述日志的原点的字符串,并使用某些引擎盖下存在的数据类型,它必须在运行时推出一些性能开销的消息。
  2. 它不允许在其他的单子比IO记录,所以你必须使用WriterT或其他解决方案不乱你的代码。
  3. 它是不可扩展的 - 你不能创建自己的优先级,定义特定行为(如线程间记录)或编译时间日志过滤。
  4. 它不提供一些信息,比如行号或文件名在日志放置。 ,当然这是很难把它扩大到支持这些信息。

话虽这么说,我很乐意介绍Logger haskell framework。它允许高效率和可扩展的日志记录,包括:

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

但库是相当新的,所以它可能缺少一些必要的功能。良好的信息是,你可以自己创建伊斯利这个功能,或者帮助我们通过报告GitHub上的要求提高了。

该记录仪是由我在(luna-lang.org)工作,并使用我们正在创造一个编译器内的公司内部开发的。


0
投票

无耻插头:我是co-log日志库的作者。你可以找到库的使用和实施在以下博客文章的详细信息:

这个库背后的主要思想是把记录操作一样简单哈斯克尔功能。因为函数是在Haskell一等公民,这是非常容易与他们一起工作。

© www.soinside.com 2019 - 2024. All rights reserved.