Ich versuche HSlogger zu verwenden, um Informationen über mein Programm zu erhalten. Deshalb füge ich meiner Funktion die folgende Zeile hinzu
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."
...
Und das funktioniert gut, weil die Funktion in IO ist. Wenn ich jedoch der folgenden Funktion eine ähnliche Zeile hinzufüge:
...
parsePES :: Parser PESFile
parsePES = do
header <- string "#PES"
warningM "parsing header"
...
return (PESFile ...)
Ich erhalte einen Tippfehler:
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";
...
Und ich verstehe vollkommen, warum - parsePES in der Parser-Monade ist, nicht in der IO Monade. Was ich nicht verstehe, ist, was ich dagegen tun soll. Benötige ich einen Monadentransformator, damit ich die Parser-Monade und die IO-Monade zusammen stapeln kann? Wie gehe ich vor?
Zunächst ein kurzer Hinweis: "Protokollieren" ist im Allgemeinen in Haskell-Code nicht sinnvoll, da es sich um eine sequenzielle Ausführung handelt, die möglicherweise von Bedeutung ist oder nicht. Stellen Sie sicher, dass Sie zwischen der Protokollierung der Programmausführung und der Protokollierung der berechneten Werte unterscheiden. In strengen imperativen Sprachen sind diese meist gleich, in Haskell jedoch nicht.
Das heißt, es hört sich so an, als ob Sie im Rahmen einer bereits sequenziellen und statusbehafteten Berechnung auf der Grundlage der zu berechnenden Werte protokollieren möchten. Dies funktioniert so ziemlich genauso wie das Protokollieren in den meisten anderen Sprachen. Sie benötigen jedoch die Monade, um dies zu unterstützen. Es sieht so aus, als wäre der von Ihnen verwendete Parser aus dem HCodecs-Paket , der relativ begrenzt zu sein scheint, IO
nicht zulässt und nicht als Monadentransformator definiert ist.
Ehrlich gesagt wäre mein Rat, die Verwendung einer anderen Analysebibliothek in Betracht zu ziehen. Parsec ist in der Regel die Standardeinstellung, und ich denke attoparsec ist für bestimmte Zwecke beliebt (einschließlich Ihrer Aktivitäten). In beiden Fällen können Sie die Protokollierung viel einfacher hinzufügen: Parsec ist ein Monadentransformator. Sie können ihn also nach Bedarf auf IO
setzen und dann liftIO
verwenden, während attoparsec auf inkrementelle Verarbeitung ausgelegt ist, sodass Sie Ihre Eingaben aufteilen und Aspekte von protokollieren können Die Verarbeitung (obwohl die Protokollierung im eigentlichen Parser möglicherweise umständlicher ist). Es gibt auch andere Möglichkeiten, aber ich kenne nicht genug Details, um eine Empfehlung abzugeben. Die meisten Parser-Combinator-basierten Bibliotheken haben in der Regel ein ziemlich ähnliches Design. Daher würde ich davon ausgehen, dass die Portierung Ihres Codes unkompliziert ist.
Eine letzte Möglichkeit, wenn Sie wirklich bleiben möchten, was Sie haben, wäre, sich die Implementierung der Parsing-Bibliothek anzuschauen, die Sie jetzt verwenden, und Ihren eigenen IO
zu rollen. -orientierte Version davon. Aber das ist wahrscheinlich nicht ideal.
Außerdem ist der in GHCi integrierte Debugger möglicherweise hilfreicher oder altmodischer, wenn Sie nicht protokollieren, was Sie wirklich wollen, sondern nur die Ausführung Ihres Programms als Teil der Entwicklung nachverfolgen printf-Debugging über das Debug.Trace-Modul .
Bearbeiten : Okay, hört sich so an, als hätten Sie plausible Gründe, über das Rollen Ihrer eigenen Variation nachzudenken. Was Sie hier grob wollen, ist ein ParserT
-Monadentransformator. Hier ist die aktuelle Definition von Parser
:
newtype Parser a = Parser { unParser :: S -> Either String (a, S) }
Der Typ S
ist der Parser-Status. Beachten Sie, dass dies in etwa eine fest codierte Version von StateT S (Either String) a
ist:
newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
... wobei Either String
als Fehlermonade behandelt wird. Der Monadentransformator ErrorT
macht dasselbe:
newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }
Wenn der aktuelle Typ also StateT S (ErrorT String Identity)
entspricht, ist das, was Sie möchten, StateT S (ErrorT String IO)
.
Es sieht so aus, als würden die meisten Funktionen des Moduls nicht mit den Interna der Parser
-Monade in Konflikt geraten. Daher sollten Sie in der Lage sein , die Typdefinitionen einfach zu ersetzen und den entsprechenden Typ anzugeben Klasseninstanzen, schreiben Sie Ihre eigene Funktion runParser
und legen Sie los.
Haftungsausschluss: Ich bin der Autor des Logger haskell frameworks .
Obwohl McCanns Antwort sehr detailliert ist, gibt es keinen Hinweis darauf, dass Haskell zum Zeitpunkt der Beantwortung der Frage kein allgemeines Protokollierungs-Framework hatte. Der HSLogger ist jetzt ein Standard, bietet jedoch sehr grundlegende Protokollierungsfunktionen, ist jedoch langsam und nicht erweiterbar. Um klar zu sein, hier sind einige Mängel von HSLogger:
WriterT
oder andere Lösungen verwenden, um Ihren Code nicht durcheinander zu bringen.Davon abgesehen würde ich gerne das Logger haskell framework einführen. Es ermöglicht eine effiziente und erweiterbare Protokollierung, einschließlich:
WriterT
monad)TemplateHaskell
name__, über die zusätzliche Details wie Dateinummern oder Modulnamen protokolliert werden könnenBaseLogger
namens__ erstellt, der nichts Sinnvolles bewirken kann. Um es klar auszudrücken: Die Filterfunktion wird in weniger als 20 Zeilen als Logger-Transformator erstellt, und Sie können Ihre eigenen Transformatoren definieren. Wie das geht, ist in der Dokumentation beschrieben.Die Bibliothek ist jedoch ziemlich neu, sodass einige der benötigten Funktionen fehlen können. Die gute Information ist, dass Sie diese Funktionalität einfach selbst erstellen oder uns helfen können, sie zu verbessern, indem Sie Anfragen auf GitHub melden.
Der Logger wurde intern von der Firma entwickelt, bei der ich arbeite ( luna-lang.org ) und wird in einem Compiler verwendet, den wir erstellen.
Schamloser Plug: Ich bin der Autor der co-log
-Protokollierungsbibliothek. Details zur Bibliotheksnutzung und -implementierung finden Sie in folgendem Blog-Beitrag:
Die Hauptidee hinter dieser Bibliothek besteht darin, Protokollierungsaktionen als einfache Haskell-Funktion zu behandeln. Da Funktionen in Haskell erstklassige Bürger sind und es extrem einfach ist, mit ihnen zu arbeiten.