簡體   English   中英

如何登錄 Haskell?

[英]How do I do logging in Haskell?

我正在嘗試使用 HSlogger 來獲取有關我的程序的一些信息。 所以我將以下行添加到我的 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."
     ...

這很好用,因為 function 在 IO 內部。 但是,當我向以下 function 添加類似的行時:

...
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 在 Parser monad 中,而不是 IO monad。 我不明白該怎么做。 我是否需要一個 monad 轉換器,以便我可以將 Parser monad 和 IO monad 堆疊在一起? 我該怎么辦?

首先,快速免責聲明:“記錄”在一般 Haskell 代碼中通常沒有意義,因為它假定某種順序執行可能有意義,也可能沒有意義。 確保區分記錄程序的執行方式記錄計算的值 在嚴格的命令式語言中,這些基本相同,但在 Haskell 中則不同。

也就是說,聽起來您想在已經順序和有狀態的計算的上下文中基於正在計算的值進行日志記錄,這與大多數其他語言的日志記錄幾乎相同。 但是,您確實需要 monad 來支持一些這樣做的方法。 看起來您正在使用的解析器來自 HCodecs package ,這似乎相對有限,不允許IO ,並且未定義為單子變壓器。

老實說,我的建議是考慮使用不同的解析庫。 Parsec往往是一種默認選擇,我認為attoparsec對於特定目的(可能包括你正在做的事情)很受歡迎。 任何一種都可以讓您更輕松地添加日志記錄:Parsec 是一個單子轉換器,因此您可以將其放在IO ,然后根據需要使用liftIO ,而 attoparsec 是圍繞增量處理設計的,因此您可以將輸入和日志方面分塊處理(盡管在實際解析器中記錄可能更尷尬)。 還有其他選擇,但我不知道足夠的細節來提出建議。 大多數基於解析器組合器的庫往往具有非常相似的設計,因此我希望移植您的代碼會很簡單。

最后一個選擇,如果你真的想堅持你所擁有的,那就是查看你現在正在使用的解析庫的實現,並推出你自己的IO版本。 但這可能並不理想。


另外,作為附錄,如果您真正追求的不是真正的日志記錄,而只是跟蹤程序的執行作為開發的一部分,您可能會發現 GHCi 中內置的調試器更有幫助,或者是老式的printf 通過Debug.Trace 模塊進行調試。


編輯:好的,聽起來您有合理的理由考慮推出自己的變體。 你在這里大致想要的是一個ParserT monad 轉換器。 這是Parser的當前定義:

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

S類型是解析器 state。 請注意,這大致是StateT S (Either String) a的硬編碼版本:

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

...其中Either String被視為錯誤單子。 ErrorT monad 轉換器做同樣的事情:

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

因此,在當前類型等同於StateT S (ErrorT String Identity)的情況下,您想要的是StateT S (ErrorT String IO)

看起來模塊中的大多數函數都沒有弄亂Parser monad 的內部,所以你應該能夠簡單地替換類型定義,提供適當的類型 class 實例,編寫你自己的runParser function,並且很好至 go。

免責聲明:我是Logger Haskell 框架的作者。

盡管 McCann 的回答非常詳細,但它並沒有說明 Haskell 在提出問題時缺乏通用日志框架。 HSLogger 現在是一個標准,但它提供了非常基本的日志記錄功能,但速度慢且不可擴展。 需要明確的是,這里有一些 HSLogger 的缺陷:

  1. 它很慢。 慢的意思是,每次你記錄一條消息時,它都會解析(以一種非常簡單的方式)一個描述日志來源的字符串,並在后台使用一些存在的數據類型,這必須在運行時引入一些性能開銷。
  2. 它不允許登錄除 IO 之外的其他 monad,因此您必須使用WriterT或其他解決方案以免弄亂您的代碼。
  3. 它不可擴展——您無法創建自己的優先級、定義自定義行為(如線程間日志記錄)或編譯時日志過濾。
  4. 它不提供一些信息,例如放置日志的行號或文件名。 當然,很難擴展它以支持此類信息。

話雖如此,我很想介紹Logger Haskell 框架 它允許高效且可擴展的日志記錄,包括:

  1. 登錄順序純代碼(執行以及使用WriterT monad)
  2. 高級消息過濾(包括編譯時過濾)
  3. 線程間日志記錄能力
  4. 提供TemplateHaskell接口,允許記錄其他詳細信息,例如文件編號或模塊名稱
  5. 非常容易擴展——所有的特性都是作為一個簡單的BaseLogger的擴展而創建的,它不能做任何明智的事情。 需要明確的是 - 過濾功能是在不到 20 行中創建的,作為 logger-transformer,您可以定義自己的轉換器。 如何做到這一點在文檔中進行了描述。
  6. 默認在所有平台上提供彩色 output。

但是這個庫是相當新的,所以它可能缺少一些需要的功能。 好消息是,您可以自己輕松創建此功能,或者通過報告 GitHub 上的問題來幫助我們改進它。

記錄器由我工作的公司 ( luna-lang.org ) 在內部開發,並在我們正在創建的編譯器中使用。

無恥插件:我是co-log日志庫的作者。 您可以在 GitHub 上找到庫的代碼和教程:

以下博客文章中描述了庫使用和實現的詳細信息:

不要害怕一個可怕的名字,這個庫實際上比聽起來簡單得多:) co-log背后的主要思想是將日志記錄操作視為簡單的 Haskell 函數。 由於函數是 Haskell 中的一等公民,因此使用它們非常容易。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM