2013-07-30 10 views
5

Supponiamo di avere il seguente codice, dove una coppia di tipi finiscono per essere posto all'interno di altri due tipi, il più esterno dei quali è un GADT:Haskell: Utilizzando l'istanza corretta per un tipo avvolto in un altro tipo

{-# LANGUAGE FlexibleInstances, 
      GADTSyntax, 
      GADTs, 
      OverlappingInstances, 
      StandaloneDeriving #-} 

data SomeType1 = SomeType1 deriving Show 
data SomeType2 = SomeType2 deriving Show 

class SomeClass d where 

instance SomeClass SomeType1 where 
instance SomeClass SomeType2 where 

data WrapperType t where 
    WrapperType :: (SomeClass t, Show t) => t -> (WrapperType t) 

instance Show (WrapperType SomeType1) where 
    show (WrapperType d) = "correct" 

instance Show (WrapperType t) where 
    show (WrapperType d) = "incorrect" 

data ListWrap where 
    ListWrap :: [(WrapperType d)] -> ListWrap 

deriving instance Show ListWrap 

Ora, scrivendo [WrapperType SomeType1] mi dà quello che voglio:

*MyModule> [WrapperType SomeType1] 
[correct] 

Ma non appena ho messo dentro ListWrap ottengo il torto Show esempio scelto per visualizzare i contenuti:

*MyModule> ListWrap [WrapperType SomeType1] 
ListWrap [incorrect] 

Deve esserci qualcosa nelle classi di tipi e/o GADT che non riesco a capire - quale potrebbe essere?

+0

tipo di risoluzione di classe è fatto al momento della compilazione e con quella esistenziale, il tipo di 'd' in' d' WrapperType viene memorizzato all'interno del 'ListWrap' è un cosa runtime – jozefg

+0

Può essere risolto con annotazioni di tipo manuale? (Inoltre, la risposta utile di seguito sembra essere stata cancellata prima che avessi la possibilità di leggerla correttamente, non so perché). –

+0

L'ho eliminato perché l'ho inviato un po 'prematuramente, non funzionava ancora anche se si contabilizzano le informazioni sul tipo cancellato. L'ho appena annullato aggiungendo quel bit in più. –

risposta

5

Se si guarda il codice derivato (con -ddump-deriv), allora si può vedere che l'istanza derivato per ListWrap è abbastanza normale in cerca

instance Show ListWrap where 
    showsPrec a (ListWrap b) = 
    showParen (a >= 11) $ showString "ListWrap " . showsPrec 11 b 

è solo di passaggio la mostra interna verso il basso per showsPrec di nuovo. Questo suggerisce che il problema è che GHC sta cancellando la variabile di tipo d quando lo si avvolge con ListWrap. In effetti, si potrebbe anche scrivere ListWrap come

data ListWrap where 
    ListWrap :: forall d. [(WrapperType d)] -> ListWrap 

che sottolinea che questo è esistenzialmente digitato.

Possiamo vedere l'errore più direttamente eliminando il instance Show (WrapperType t) e osservando tipo di errore del GHC

/Users/tel/tmp/foo.hs:33:52: Warning: 
    No instance for (Show (WrapperType d)) 
     arising from a use of `showsPrec' 
    Possible fix: 
     add an instance declaration for (Show (WrapperType d)) 
    In the second argument of `(.)', namely `showsPrec 11 b' 
    In the second argument of `($)', namely 
     `showString "ListWrap " . showsPrec 11 b' 
    In the expression: 
     showParen (a >= 11) $ showString "ListWrap " . showsPrec 11 b 
Ok, modules loaded: Main. 

In altre parole, ha perso i dettagli sul tipo d e quindi non può unificare la specifica instance Show (WrapperType SomeType1).


Ora si potrebbe pensare che ciò implicherebbe che mantenere tali informazioni in giro renderebbe l'errore di tipo scomparire.

data ListWrap d where 
    ListWrap :: [(WrapperType d)] -> ListWrap d 

> show $ ListWrap [WrapperType SomeType1] 
"ListWrap [incorrect]" 

Ma sembra che anche la ricerca dell'istanza stia andando male. L'unico modo che ho trovato per farlo funzionare era quello di attivare UndecidableInstances e fornire un suggerimento per derivazione di istanza.

deriving instance Show (WrapperType d) => Show (ListWrap d) 

dopo di che l'esempio funziona

> show $ ListWrap [WrapperType SomeType1] 
"ListWrap [correct]" 
+0

Va notato che * ogni * tipo di variabile in una dichiarazione GADT è implicitamente universalmente quantificato. Non c'è scope come nei normali ADT (quindi scrivere il forall non enfatizza molto, più di quanto farebbe con una dichiarazione di funzione regolare). – shachaf

+0

Qual è il nucleo dell'implementazione non esistenziale di 'deriving Show'? Mi chiedo se il tipo viene cancellato comunque o se è solo l'algoritmo di risoluzione del tipo di classe che fa cose strane. – jozefg

+0

Fantastico, grazie per aver trovato un modo per farlo funzionare! Non avevo idea di poter usare le variabili di tipo all'interno di altri tipi come argomenti alle classi (nell'istanza derivante), quindi non capisco esattamente cosa sta succedendo lì. –

Problemi correlati