Un esistenziale probabilmente non è quello che vuoi qui; non è possibile "osservare" i tipi effettivi associati a e
o w
in un valore Dangerous a
, quindi si è completamente limitati alle operazioni fornite da Error
e Show
.
In altre parole, l'unica cosa che sai di w
è che si può trasformarlo in un String
, quindi potrebbe anche essere solo un String
(ignorando la precedenza per semplificare le cose), e l'unica cosa che conosci e
è possibile trasformarlo in un String
, è possibile attivare String
s e si dispone di un valore distinto di esso (noMsg
). Non c'è modo di affermare o verificare che questi tipi siano uguali a tutti gli altri, quindi una volta che li hai inseriti in un Dangerous
, non c'è modo di recuperare alcuna struttura speciale di quei tipi.
Quello che il messaggio di errore sta dicendo è che, in sostanza, il tipo di runDangerous
crediti che è possibile trasformare un Dangerous
in un (Either e a, [w])
per qualsiasie
e w
che hanno le istanze competenti. Questo chiaramente non è vero: puoi solo convertire un Dangerous
in quel tipo per uno scelta di e
e w
: quello con cui è stato creato. Il w1
è solo perché il tuo tipo Dangerous
è definito con una variabile di tipo w
, e così è runDangerous
, quindi GHC rinomina uno di essi per evitare conflitti di nome.
Il tipo è necessario dare runDangerous
assomiglia a questo:
runDangerous
:: (forall e w. (Error e, Show e, Show w) => (Either e a, [w]) -> r)
-> Dangerous a -> r
che, data una funzione che accetta un valore di tipo (Either e a, [w])
per eventuali scelte di e
e w
fintanto che hanno la istanze fornite e un Dangerous a
produce il risultato di quella funzione. Questo è abbastanza difficile da capire!
L'implementazione è semplice come
runDangerous f (Dangerous m) = f $ runState (runErrorT m) []
che è un cambiamento banale per la vostra versione. Se questo funziona per te, ottimo; ma dubito che un esistenziale sia il modo giusto per ottenere qualunque cosa tu stia cercando di fare.
Nota che è necessario il {-# LANGUAGE RankNTypes #-}
per esprimere il tipo di runDangerous
. In alternativa, è possibile definire un altro esistenziale per il vostro tipo di risultato:
data DangerousResult a = forall e w. (Error e, Show e, Show w) =>
DangerousResult (Either e a, [w])
runDangerous :: Dangerous a -> DangerousResult a
runDangerous (Dangerous m) = DangerousResult $ runState (runErrorT m) []
ed estrarre il risultato con case
, ma dovrete stare attenti, o GHC inizierà lamentando che hai lasciato e
o w
fuga - che è l'equivalente del tentativo di passare una funzione insufficientemente polimorfica all'altra forma di runDangerous
; cioè uno che richiede più vincoli su ciò che e
e w
sono oltre ciò che garantisce il tipo di runDangerous
.
C'è un modo migliore (o più idiomatico) per farlo? Voglio solo un monito di errore che viene fornito con alcuni avvertimenti. – So8res
Perché non definire semplicemente il tipo "Dangerous e w a'? Non c'è bisogno di esistenziali qui, se capisco cosa stai cercando di ottenere (cosa che probabilmente non potrei fare). – ehird
Ho alcuni moduli che lanciano tutti i propri errori e avvertimenti e vengono gestiti al livello più alto. Il livello principale deve solo stamparli, ma è fastidioso dire "Dangerous OptError OptWarning [Opzione]" nel modulo delle opzioni e "Dangerous TemplateError TemplateWarning Template" nel file templates, quando saranno tutti semplicemente "show'n. Sto cercando di rimuovere un sacco di informazioni e di imparare qualcosa, non è certamente essenziale. – So8res