2010-01-24 10 views
17

Sto leggendo Real World Haskell Pg 151, e ho fissò il seguente passaggio per più di un'ora:Qual è l'effetto dei sinonimi di tipo su istanze di classi di tipi? Che cosa fa il pragma di TypeSynonymInstances in GHC?

Ricordiamo che String è un sinonimo di [Char], che a sua volta è il tipo [a] dove Char è sostituito per il tipo parametro a. Secondo le norme Haskell 98, non c'è permesso di fornire un tipo in luogo di un parametro di tipo quando si scrive un'istanza. In altre parole, sarebbe legale per noi scrivere un'istanza per [a], ma non per [Char]. 16 commenti 5335

Semplicemente non sta affondando in. Fissando il the (free not pirated) copy of RWH chapter 6 vedo un sacco di altre persone sono davvero soffrendo con questo. Ancora non lo capisco dai commenti ...

In primo luogo, tutto ciò mi confonde, quindi per favore se senti di poter spiegare qualcosa su questo passaggio, o TypeSynonymInstances per favore.

Ecco il mio problema:

  • Int è un costruttore di dati
  • String è un costruttore di dati Etipo sinonimo

ora non posso rispondi a queste domande:

  1. Perché un sinonimo di tipo preclude la creazione del tipo di un membro di una classe di tipo (sto cercando una qualche ragione che probabilmente si riferisce alla compilazione o all'impianto di un sinonimo di tipo)?
  2. Perché i progettisti del linguaggio, non vogliono questa sintassi (sto chiedendo di ragionamento non molto ampio teoria o unicode matematica simboli).
  3. Vedo questa riga "il tipo [a] dove Char è sostituito per il parametro di tipo a", e voglio sapere perché non posso sostituirlo per questo "il tipo a dove Int è sostituito per il parametro di tipo a "".

Grazie!

+1

Nel mio codice, ho trovato che usare le istanze di tipo sinonimo significa che voglio veramente usare 'newtype' (e forse derivato newtype generalizzato) invece di' type'. Se non hai familiarità con la differenza tra 'type' e' newtype', questo dovrebbe essere il tuo primo passo. – jrockway

risposta

27

Credo che parte del problema è che i due, in gran parte non correlata, le restrizioni sono in gioco:

  • Nessun tipo casi sinonimo significa che le istanze possono essere solo cose dichiarate con data o newtype, non type. Questo impedisce String, ma non [Char].
  • Nessuna istanza flessibile significa che le istanze possono menzionare solo un tipo che non è una variabile e solo tale tipo può essere utilizzato come un costruttore di tipi. Questo impedisce Maybe Int e f Int, ma non Maybe a.

Ecco cosa dice a proposito GHCi Int, Char, e String:

data Char = GHC.Types.C# GHC.Prim.Char# 
data Int = GHC.Types.I# GHC.Prim.Int# 
type String = [Char] 

Int e Char sono semplici tipi senza tipo parametri variabili; non c'è alcun tipo di costruttore coinvolto, quindi puoi creare istanze con loro praticamente liberamente.

La stringa, tuttavia, non riesce su entrambi conta. È un sinonimo di tipo, che non è consentito ed è anche un costruttore di tipo applicato a una variabile non, vale a dire il costruttore del tipo di elenco applicato a Char.

Per confronto, si noti che [a], Maybe a e Either a b sono tutte valide in casi, ma [Int], Maybe [a], e Either String a sono vietati; si spera che ora puoi vedere perché.

Per quanto riguarda le vostre domande dirette, non so quali fossero le motivazioni originali per progettare la lingua in quel modo, e non sono in alcun modo qualificato per fare affermazioni autorevoli sulle "migliori pratiche", ma per il mio personale codifica io in realtà non esitate ad utilizzare questi pragma:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
{-# LANGUAGE EmptyDataDecls #-} 
{-# LANGUAGE TypeSynonymInstances #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE FlexibleContexts #-} 

Si può sempre andare a vedere packages that use pragmas. Le istanze flessibili, a quanto pare, ottengono una buona dose di utilizzo, e da pacchetti "rispettabili" (ci sono un paio di hit nel sorgente per Parsec, per esempio).

+0

Mi sento come se non capissi ancora completamente questa risposta.Ma è così ben scritto che voglio farne la mia risposta scelta anche se ritengo che il pezzo pratico sia essenziale per la domanda troppo composta, quindi mi limiterò a riformulare la domanda senza di essa. –

+0

Beh, anche se non posso davvero parlare alle migliori pratiche, vedere ciò che le altre persone stanno facendo potrebbe aiutare; Ho appena aggiunto un po 'di questa risposta. –

+0

@ C.A.McCann - la riga "Ecco cosa dice GHCi su Int, Char e String", cosa hai scritto al prompt GHCi per ottenere quelle risposte? –

9

In realtà, né IntString sono costruttori di dati. Cioè, non è possibile utilizzarli per creare un valore di

> (Int 42, String "bob") 
<interactive>:1:1: Not in scope: data constructor `Int' 
<interactive>:1:9: Not in scope: data constructor `String' 

Int nomi un nuovo, distinto, algebrica dei tipi di dati. String è un "sinonimo di tipo" o alias per il tipo già esistente: [Char]. Il problema è che Haskell 98 dice che non è possibile utilizzare un sinonimo di tipo in una dichiarazione di istanza.

Non posso dire perché gli autori del rapporto Haskell 98 scelgono di limitare i sinonimi di tipo in questo caso. Ci sono un certo numero di restrizioni su di loro. Ad esempio, non possono essere parzialmente applicati (se accettano argomenti di tipo). Penso che un indizio arrivi alla fine del §4.2.2:

Sinonimi di tipo sono una comoda, ma rigorosamente sintattica, meccanismo per rendere tipo firme più leggibile. Un sinonimo di e la sua definizione sono completamente intercambiabili, tranne nel il tipo di istanza di una dichiarazione di istanza (Sezione 4.3.2).

Presumibilmente, c'era un approccio alla compilazione del programma, per il quale questa interscambiabilità sintattica avrebbe causato problemi per le istanze. Forse ha a che fare con aspetto notevole di istanze che essi fuoriuscire pacchetti ...

Quanto alla tua ultima domanda, credo che la spiegazione è una confusione fra due cose: 1) String è un tipo sinonimo di [Char], che è a sua volta una specializzazione del tipo più generale [a] e 2) che anche senza il sinonimo, [Char] non può essere utilizzato nella testa di un'istanza.

Questo secondo problema non ha nulla a che fare con i sinonimi di tipo, ma le teste di istanza devono avere tutti i parametri di tipo per il costruttore di tipi be variabili, non tipi concreti. Cioè, non puoi definire istanze separate per [Int] e [Char] per alcune classi, puoi solo definire istanze [a]. (Ricorda che, nonostante la comoda sintassi, [] è un costruttore di tipi e la cosa all'interno è il parametro type.)

Ancora, non so perché il rapporto li limiti, ma sospetto che debba fare anche questo con la strategia di compilazione. Dal momento che la strategia di compilazione di GHC per le istanze può gestirlo, puoi rilassare questo vincolo in GHC tramite -XFlexibleInstances.

Infine, ho visto entrambe le estensioni accesi in un bel po 'di codice, ma forse qualcuno con più esperienza Haskell possono pesare in quanto a se sono "buone pratiche" o no.

+0

Un altro bel resoconto, grazie mille, la tua conoscenza è una risorsa per StackOverflow. Domanda veloce però: 'cioè, puoi definire istanze separate per [Int] e [Char] per alcune classi', è che si supponga di leggere' can', o 'can't', perché sembra essere in conflitto con il resto del paragrafo. Penso che [Int] e [Char] siano tipi concreti, il che significherebbe che non funzionerebbero come istanze di classi di tipi? –

+0

buona presa fissa! – MtnViewMark

1

Haskell testa 98 ​​

  1. un'istanza deve avere la forma C (T u1 ... uk), dove T è un tipo di costruzione definito da una dichiarazione di dati o newtype (vedi TypeSynonymInstances) e la ui sono variabili di tipo distinte e
  2. ogni asserzione nel contesto deve avere la forma C 'v, dove v è uno di ui.

quindi è valido per utilizzare instance ClassName TypeConstructor where e TypeConstructor MUST essere tale da Int, Double o [a], assicurarsi che solo può un costruttore di tipo essere coinvolto !!

proposito, è [] tipo di costruzione, quindi [TypeConstructor] non possono essere utilizzati ma [NewType] e [TypeVariable] sono ammessi.

Questa è una restrizione è Haskell e possiamo evitarlo abilitando FlexibleInstances.

Problemi correlati