2015-02-13 15 views
6

Si consideri il seguente codice Haskell:Dipendenza funzionale Haskell a b -> c secondo c?

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, 
    FunctionalDependencies #-} 

class C a b c | a b -> c 

instance C (l (i,j)) (r i j) j 
instance C (l i j) (r (i,j)) j 
-- Conflict between the following two lines 
instance C (l (i,j)) (r (i,j)) j 
instance C (l i j) (r i j) j 

Qui, GHC produce un errore di dipendenze funzionali tra le ultime due righe. Se si rilascia una delle ultime due dichiarazioni di istanza, il codice viene compilato. Ho provato un analogo usando famiglie di tipi, che hanno anche prodotto un conflitto. La mia prima domanda è: perché le ultime due righe sono in conflitto, mentre le altre dichiarazioni funzionano tutte insieme?

Inoltre, se cambio l'ultima linea per

instance C (l i j) (r i j) i 

GHC accetta il codice. Questo sembra abbastanza strano, poiché l'unica cosa che cambia è la variabile di tipo dipendente c. Qualcuno può spiegare questo comportamento?

+0

Giusto per essere sicuro. Se rimuovi entrambe le prime due istanze c'è ancora un errore, giusto? – genisage

+0

@genisage Sì, dipende solo dalle ultime due istanze –

+0

Non riesco a riprodurre la seconda parte qui. 'istanza C (l i j) (r i j) i' causa un conflitto per me. (su ghc 7.8.3) – genisage

risposta

6

Le ultime due istanze hanno un'unione in conflitto. Mi permetta di utilizzare completamente diversi nomi di variabili:

C (a c (d,e)) (b c (d,e)) e 
vs. 
C (a c (d,e)) (b c (d,e)) (d,e) 

In particolare, il vostro l dalla terza istanza può essere unificato con un tipo di costruzione che ha già un argomento applicata.

Cambiare il j-i fa l'ultimo invece:

C (a c (d,e)) (b c (d,e)) c 

io ancora non capisco perché questo non dà una denuncia. Forse perché è possibile assegnare i tipi tali che c = e, ma non tale che e = (d,e) (che darebbe un tipo infinito che Haskell non consente), ma sembra ancora una cosa dubbiosa da consentire. Forse questo è anche un bug di GHC.

Le altre combinazioni di istanze non sono in conflitto perché quando si tenta di unificarle, si ottengono contraddizioni simili allo e = (d,e) precedente, ma nelle parti non dipendenti, quindi non possono corrispondere.

+0

Sarei d'accordo con te. Sperimentando, ho trovato che 'classe C a b; istanza C (l (i, j)) (r (i, j)); l'istanza C (l i j) (r i j) 'è _accettata_ senza menzionare la sovrapposizione (!?). Forse ora alcune sovrapposizioni vengono rilevate solo più tardi, quando viene chiamato un metodo (solo indovinando qui). – chi

+0

Grazie per aver segnalato il processo di unificazione. @chi: In effetti, se provo a scrivere un metodo di classe 'f :: a -> b -> c', ricevo un reclamo incoerenza quando chiamo' f' –