wake-up-neo.com

Wie melde ich mich in Haskell an?

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?

42
nont

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.

50
C. A. McCann

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:

  1. Es ist langsam. Mit langsam meine ich, dass jedes Mal, wenn Sie eine Nachricht protokollieren, eine Zeichenfolge analysiert wird, die den Ursprung des Protokolls beschreibt, und einige existenzielle Datentypen verwendet werden, die zur Laufzeit einen gewissen Performance-Overhead verursachen müssen.
  2. Andere Monaden als IO können nicht angemeldet werden, daher müssen Sie WriterToder andere Lösungen verwenden, um Ihren Code nicht durcheinander zu bringen.
  3. Es ist nicht erweiterbar. Sie können keine eigenen Prioritätsstufen erstellen, benutzerdefinierte Verhaltensweisen definieren (z. B. Protokollierung zwischen Threads) oder Zeitprotokolle filtern.
  4. Es enthält keine Informationen wie Zeilennummern oder Dateinamen, in denen die Protokolle abgelegt wurden. Und natürlich ist es sehr schwer, es zu erweitern, um solche Informationen zu unterstützen.

Davon abgesehen würde ich gerne das Logger haskell framework einführen. Es ermöglicht eine effiziente und erweiterbare Protokollierung, einschließlich:

  1. anmelden in sequentiellem reinem Code (sowohl mit als auch mit WriterTmonad)
  2. erweiterte Nachrichtenfilterung (einschließlich Filterung zur Kompilierungszeit)
  3. möglichkeit zur Protokollierung zwischen Threads
  4. bietet die Schnittstelle TemplateHaskellname__, über die zusätzliche Details wie Dateinummern oder Modulnamen protokolliert werden können
  5. ist sehr einfach erweiterbar - alle Funktionen werden als Erweiterung eines einfachen BaseLoggernamens__ 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.
  6. Bietet standardmäßig Farbausgabe auf allen Plattformen.

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.

24
Wojciech Danilo

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.

0
Shersh