Sto scrivendo un programma che convalida una complessa struttura di dati in base a una serie di regole complesse. Inserisce i dati e genera un elenco di messaggi che indicano problemi con i dati.Posso riflettere i messaggi di un programma Haskell in fase di runtime?
pensare in questa direzione:
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"
Per la documentazione, voglio anche di compilare una lista di tutti i messaggi di questo programma può produrre. Cioè, voglio ottenere il valore:
[ Message E 1001 "negative age"
, Message W 1002 "age too large"
, Message E 1003 "empty first name"
, Message E 1004 "empty nickname"
]
potrei spostare i messaggi fuori checkPerson
in qualche struttura di dati esterni, ma mi piace quando i messaggi sono definiti proprio nel punto in cui vengono utilizzati.
Potrei (e probabilmente dovrei) estrarre i messaggi dall'AST in fase di compilazione.
Ma la flessibilità propagandata di Haskell mi ha fatto riflettere: posso ottenere quello in fase di esecuzione? Cioè, posso scrivere una funzione
allMessages :: (Person -> Validator()) -> [Message]
tale che allMessages checkPerson
mi avrebbe dato la lista di cui sopra?
Naturalmente, checkPerson
e Validator
non deve rimanere lo stesso.
posso quasi (non del tutto) vedere come avrei potuto fare un Validator
monade con una “backdoor” che avrebbe gestito checkPerson
in una sorta di “modalità di riflessione”, che attraversa tutti i percorsi e ritorno tutto Message
s incontrato. Dovrei scrivere una funzione personalizzata when
che saprebbe ignorare il suo primo argomento in alcune circostanze (quali?). Quindi, una specie di DSL. Forse potrei anche emulare il pattern matching?
Quindi: posso fare qualcosa di simile, come, e cosa dovrei sacrificare?
Non esitate a suggerire soluzioni, anche se non si adattano esattamente alla descrizione precedente.
Questo è un problema piuttosto difficile in generale, in sostanza si sta cercando di scrivere uno strumento di analisi statico per il proprio DSL. Potresti scrivere un DSL di questo tipo in Haskell usando le monade libere abbastanza facilmente, in realtà, ma eseguendo l'analisi per estrarre tutti i messaggi possibili, tuttavia sarà difficile dal momento che il valore di un messaggio può essere determinato esclusivamente in fase di runtime. Se limiti i tuoi 'title's e' code's usando semplici tipi di dati di somma, allora sarebbe un po 'più semplice, ma hai ancora il problema che alcuni valori potrebbero essere determinati solo dai valori di runtime. – bheklilr
@bheklilr Spero che la mia risposta faccia esplodere la tua mente. =) –
@DanielWagner fa un po ', sì! Non avrei pensato a quell'approccio. – bheklilr