2012-05-07 13 views
5

Upon cercando di utilizzare Data.Has, ho scritto il codice come il seguente:tipo famiglie Haskell, comprendere il messaggio di errore

data Name = Name; type instance TypeOf Name = Text 
type NameRecord = FieldOf Name; 

ho trovato:

instance I NameRecord where 
    ... 

genera un errore di compilazione, vale a dire:

Illegal type synonym family application in instance

considerando quanto segue:

012.
instance (NameRecord ~ a) => I a where 
    ... 

Compila bene.

Credo che l'errore sia correlato al ticket this in GHC, contrassegnato come non valido.

La risposta al biglietto dice:

I am not sure what you are suggesting. We cannot automatically transform

instance C (Fam Int) -- (1) 

into

instance (Fam Int ~ famint) => C famint -- (2) 

This works if there is only one instance, but as soon as there are two such instances, they always overlap.

Maybe you are suggesting that we should do it anyway and programmers should just take the implicit transformation into account. I don't think that this is a good idea. It's confusing for very little benefit (as you can always write the transformed instance yourself with little effort).

Qualcuno può approfondire questa spiegazione, magari con un po 'di codice di esempio in cui la (1) non riesce, ma (2) non lo fa, e perché?

risposta

4

Un caso in cui (1) fallisce ma (2) non è banale; perché il tipo sinonimi (type ExampleOfATypeSynonym = ...) non sono ammessi nelle dichiarazioni di istanza, ma essi sono autorizzati a vincoli, qualsiasi situazione in cui si ha solo una esempio così:

-- (1) 
class Foo a 
type Bla =() 
instance Foo Bla 

... può essere trasformato in:

-- (2) 
class Foo a 
type Bla =() 
instance (a ~ Bla) => Foo a 

L'unico motivo per cui (1) non riesce è perché sinonimi di tipo non sono ammessi nelle dichiarazioni di istanza, e questo perché di tipo sinonimi sono come funzioni di tipo: essi forniscono una mappatura di sola andata da un nome di tipo per un nome di tipo, quindi se hai un type B = A e un instance Foo B, non è ovvio che venga creata un'istanza di Foo A. La regola esiste in modo che sia necessario scrivere instance Foo A per rendere chiaro che che è il tipo che riceve effettivamente l'istanza.

L'uso di famiglie di tipi è irrilevante in questo contesto, poiché il problema è piuttosto che si sta utilizzando un sinonimo di tipo, il tipo NameRecord. Devi anche tenere a mente che se il sinonimo di tipo viene rimosso e sostituito da FieldOf Name direttamente, la compilazione fallirà ancora; questo perché una "famiglia tipo" è solo una versione avanzata di sinonimi tipo, quindi FieldOf Name è anche un "sinonimo di tipo" per Name :> Text in questo contesto. Devi invece utilizzare una famiglia di dati e un'istanza di dati per ottenere un'associazione "bidirezionale".

Ulteriori informazioni sulle famiglie di dati sono disponibili nello GHC documentation.


Penso tu voglia dire "... dove (2) fallisce ma (1) non ..."

Immaginiamo che abbiamo una classe di tipo in questo modo:

class Foo a where 
    foo :: a 

Ora, è possibile scrivere le istanze in questo modo:.

instance Foo Int where 
    foo = 0 

instance Foo Float where 
    foo = 0 

main :: IO() 
main = print (foo :: Float) 

Questo funziona come ci si aspetterebbe Tuttavia, se si trasformare il codice in questo:

{-# LANGUAGE FlexibleInstances, TypeFamilies #-} 
class Foo a where 
    foo :: a 

instance (a ~ Int) => Foo a where 
    foo = 0 

instance (a ~ Float) => Foo a where 
    foo = 0 

main :: IO() 
main = print (foo :: Float) 

non viene compilato, viene visualizzato l'errore:

test.hs:5:10: 
    Duplicate instance declarations: 
     instance a ~ Int => Foo a -- Defined at test.hs:5:10-27 
     instance a ~ Float => Foo a -- Defined at test.hs:8:10-29 

Quindi, questo è l'esempio che si sperava stessimo cercando. Ora, questo succede solo se c'è più di un'istanza di Foo che usa questo trucco. Perché?

Quando GHC risolve le classi di tipi, esamina solo la testata di istanza; cioè ignora tutto prima dello =>. Quando ha scelto un'istanza, "si impegna" su di essa e controlla i vincoli prima dello => per vedere se sono veri. Così, in un primo momento vede due istanze:

instance Foo a where ... 
instance Foo a where ... 

E 'chiaramente impossibile decidere quale istanza da utilizzare in base a queste informazioni da solo.

+0

Per motivi di interesse, sapete se il metodo GHC di risoluzione delle classi di tipi è una decisione di progettazione esplicita? E se è così, la ragione per questo? (O forse l'alternativa è troppo complicata per essere implementata in modo pulito?) – huon

+3

La ragione è che il report Haskell richiede che si comporti in questo modo. Il motivo per ** that ** è che se non si comportava in questo modo, sarebbe necessario un algoritmo che fornisse un'euristica per "quanto bene un tipo si adatta a un vincolo"; dovresti discutere "Sì, questo tipo si adatta a questa istanza di classe, ma quell'altra istanza si adatta molto meglio perché {meno vincoli, minore distanza di riduzione, ...}." Potrebbe essere possibile sviluppare una tale euristica, ma si romperebbe l'Assunzione mondiale aperto che è un concetto chiave quando si tratta di classi di tipi. – dflemstr

+0

Immaginate 'istanza String ~ a => Foo a' e' istanza a ~ [b] => Foo a'. Questo è un esempio di casi in cui è necessario un algoritmo per risolvere 'Foo [Char]'. – dflemstr

Problemi correlati