Ich schreibe ein Programm, das eine komplexe Datenstruktur nach einer Reihe komplexer Regeln validiert. Er gibt die Daten ein und gibt eine Liste von Nachrichten aus, die Probleme mit den Daten anzeigen.Kann ich zur Laufzeit Meldungen aus einem Haskell-Programm reflektieren?
Denken in diese Richtung:
import Control.Monad (when)
import Control.Monad.Writer (Writer, tell)
data Name = FullName String String | NickName String
data Person = Person { name :: Name, age :: Maybe Int }
data Severity = E | W | C -- error/warning/comment
data Message = Message { severity :: Severity, code :: Int, title :: String }
type Validator = Writer [Message]
report :: Severity -> Int -> String -> Validator()
report s c d = tell [Message s c d]
checkPerson :: Person -> Validator()
checkPerson person = do
case age person of
Nothing -> return()
Just years -> do
when (years < 0) $ report E 1001 "negative age"
when (years > 200) $ report W 1002 "age too large"
case name person of
FullName firstName lastName -> do
when (null firstName) $ report E 1003 "empty first name"
NickName nick -> do
when (null nick) $ report E 1004 "empty nickname"
Zur Dokumentation, möchte ich auch dieses Programm ausgeben kann eine Liste aller Nachrichten kompilieren. Das heißt, ich mag den Wert erhalten:
[ Message E 1001 "negative age"
, Message W 1002 "age too large"
, Message E 1003 "empty first name"
, Message E 1004 "empty nickname"
]
ich die Nachrichten aus checkPerson
in eine externe Datenstruktur bewegen konnte, aber ich mag es, wenn die Nachrichten direkt an Ort und Stelle festgelegt werden, in denen sie verwendet werden.
Ich könnte (und wahrscheinlich sollte) die Nachrichten aus dem AST zur Kompilierzeit extrahieren.
Aber die angepriesene Flexibilität von Haskell ließ mich denken: kann ich das erreichen zur Laufzeit? Das heißt, kann ich eine Funktion
allMessages :: (Person -> Validator()) -> [Message]
so schreiben, dass allMessages checkPerson
würde ich die oben aufgeführte Liste?
Natürlich checkPerson
und Validator
müssen nicht bleiben gleich.
Ich kann fast (nicht ganz) sehen, wie ich eine benutzerdefinierte Validator
Monade mit einer „Hintertür“ machen könnte, die checkPerson
in einer Art laufen würde „Reflexionsmodus“ alle Wege durchlaufen und die Rückkehr alle Message
begegnet s. Ich müsste eine benutzerdefinierte when
-Funktion schreiben, die unter bestimmten Umständen wissen könnte (welches?), Ihr erstes Argument zu ignorieren. Also, eine Art DSL. Vielleicht könnte ich sogar Mustervergleiche emulieren?
Also: kann ich so etwas tun, wie und was hätte ich zu opfern?
Bitte zögern Sie nicht Lösungen vorschlagen, auch wenn sie nicht genau der obigen Beschreibung entsprechen.
Dies ist ein ziemlich schwieriges Problem im Allgemeinen ist, im Wesentlichen wollen, sind Sie ein statisches Analyse-Tool für Ihren DSL zu schreiben. Sie könnten eine solche DSL in Haskell schreiben, indem Sie einfach kostenlose Monaden verwenden, aber die Analyse wird durchgeführt, um alle möglichen Nachrichten herauszuziehen, aber das wird schwierig, da der Wert einer Nachricht nur zur Laufzeit bestimmt werden kann. Wenn Sie Ihre Titel und Codes mit einfachen Summendatentypen einschränken, wäre es etwas einfacher, aber Sie haben immer noch das Problem, dass einige Werte nur durch Laufzeitwerte bestimmt werden können. – bheklilr
@bheklilr Ich hoffe, dass meine Antwort Ihre Gedanken bläst. =) –
@DanielWagner es tut ein bisschen, yeah! An diesen Ansatz hätte ich überhaupt nicht gedacht. – bheklilr