2011-12-17 11 views
20

Sto provando a passare l'esempio YesNo dal libro Learn You a Haskell for Great Good!.Variabile di tipo ambiguo `a0 'nei vincoli

Ecco il mio codice sorgente:

module Main where 

main :: IO() 

main = putStrLn (show (yesno 12)) 

class YesNo a where 
    yesno :: a -> Bool 


instance YesNo Bool where 
    yesno b = b 

instance YesNo [a] where 
    yesno [] = False 
    yesno _ = True 


instance YesNo Int where 
    yesno 0 = False 
    yesno _ = True 

Quando eseguo questo codice seguente eccezione si verifica:

Ambiguous type variable `a0' in the constraints: 
    (YesNo a0) arising from a use of `yesno' 
      at /Users/mkhadikov/Projects/personal/haskell/hello-world/yesno.hs:5:25-29 
    (Num a0) arising from the literal `12' 
      at /Users/mkhadikov/Projects/personal/haskell/hello-world/yesno.hs:5:31-32 
Probable fix: add a type signature that fixes these type variable(s) 
In the first argument of `show', namely `(yesno 12)' 
In the first argument of `putStrLn', namely `(show (yesno 12))' 
In the expression: putStrLn (show (yesno 12)) 

Si può spiegare che cosa c'è di sbagliato con questo codice?

risposta

31

Il problema è che non sa che tipo 12 è! Può essere qualsiasi tipo con un'istanza Num:

È necessario specificare il tipo che si desidera direttamente: provare putStrLn (show (yesno (12 :: Int))).

Perché GHC non può selezionare Int, dal momento che nessuna altra scelta potrebbe funzionare, chiedi? Buona domanda. La risposta è che con il sistema TypClass di Haskell, l'aggiunta di un'istanza non può mai invalidare i programmi corretti esistenti o modificarne il comportamento. (Si parla di open world). Se ha scelto Int, cosa succederebbe se avessi aggiunto instance YesNo Integer? La scelta diventerebbe ambigua e il tuo programma si spezzerebbe!

Quindi, quando si desidera utilizzare una classe di caratteri come questa con un valore polimorfico, è necessario specificare che tipo si intende più precisamente. Questo non dovrebbe venire molto nella pratica, dato che di solito ci sarà un contesto circostante per forzare il tipo a essere quello che vuoi; sono principalmente i letterali numerici che sono influenzati da questo.

5

Il problema è che 12 ha effettivamente il tipo Num a => a e non Int come previsto. Se si aggiunge un tipo di annotazione esplicita come 12 :: Int, dovrebbe essere compilato.

+0

E 'possibile creare il proprio tipo di costruttori di dati che funzionano come '12' in Haskell? Sembra che ogni volta che si crea un costruttore di dati, si costruisce un valore di un singolo tipo. Ma quando scrivi '12', come puoi vedere costruisce non un valore di un singolo tipo, ma un valore di qualsiasi tipo in cui il tipo è vincolato da Num. Quindi 'Num a => a'. Sto interpretando correttamente questo? È un po 'come costruire un valore che ha un'unione esistenziale? – CMCDragonkai

+0

@CMCDragonkai Sono un po 'confuso sui dettagli ma fondamentalmente il 'Num' typeclass contiene una funzione' fromInteger :: Num a => Integer -> a'. Un letterale numerico funziona come se "fromInteger" fosse chiamato su di esso. – fuz

+0

Ah, quindi il 'a' diventa tipo esistenziale/unione. Molto interessante. – CMCDragonkai

1

Ho avuto lo stesso problema.

questa è una soluzione, forse non il migliore ma funziona:

class YesNo a where 
    yesno :: a -> Bool 


instance YesNo Int where 
    yesno 0 = False 
    yesno _ = True 


instance YesNo Integer where 
    yesno 0 = False 
    yesno _ = True 
Problemi correlati