2013-01-20 7 views
25

Quello di cui sto parlando è che non è possibile definire:Perché GHC Haskell non supporta i nomi dei parametri dei record sovraccaricati?

data A = A {name :: String} 
data B = B {name :: String} 

So che GHC solo desugars questo per funzioni semplici e il modo idiomatico per risolvere questo sarebbe:

data A = A {aName :: String} 
data B = B {bName :: String} 

class Name a where 
    name :: a -> String 

instance Name A where 
    name = aName 

instance Name B where 
    name = bName 

Dopo averlo scritto non mi piace più di tanto ... non è possibile che questa tipizzazione faccia parte del processo di desugaring?


Il pensiero mi è venuta quando stavo scrivendo alcuni Aeson JSON parsing. Dove sarebbe stato troppo facile solo per derive the FromJSON instances per ogni tipo di dati, ho dovuto scrivere tutto a mano (attualmente> 1k linee e conteggio). Avere nomi come name o semplicemente value in un record di dati non è così raro.

http://www.haskell.org/haskellwiki/Performance/Overloading indica che l'overloading delle funzioni introduce un sovraccarico di runtime. Ma in realtà non vedo perché il compilatore non sarebbe in grado di risolverlo al momento della compilazione e dare loro nomi diversi internamente.

This SO question from 2012 più o meno stati storico motivi e punti a un thread di posta elettronica del 2006. È cambiato qualcosa di recente?

Anche se ci sarebbe un sovraccarico di runtime, la maggior parte delle persone non si preoccuperebbe perché la maggior parte del codice difficilmente è critica dal punto di vista delle prestazioni.

C'è qualche estensione di lingua nascosta che in realtà consente questo? Ancora una volta non sono sicuro ... ma penso che Idris lo faccia davvero?

+0

A parte: qualcuno potrebbe aggiungere un tag Idris a SO e questa domanda? Forse qualcuno da quella comunità potrebbe anche elaborare. – fho

+0

Congratulazioni per essere la prima domanda di idratazione. Se vuoi saperne di più sull'argomento c'è una pagina sul [ghc wiki] (http://hackage.haskell.org/trac/ghc/wiki/Records) e la [conversazione su reddit] (http: // www.reddit.com/r/haskell/comments/kgd4g/the_records_problem_in_haskell_help_build_a/). – Davorak

+0

Grazie per i collegamenti. I documenti GHC non sembrano avere un buon posizionamento nei risultati di Google. – fho

risposta

3

Utilizzando la sintassi record di

data A { name :: String } 

implicitamente definisce una funzione

name :: A -> String 

Se definire sia A e B con un { name :: String }, abbiamo le definizioni contrastanti tipo per name:

name :: A -> String 
name :: B -> String 

Lo è Non è chiaro come le vostre proposte le classi di tipo implicito avrebbe funzionato perché se definiamo due tipi

data A { name :: String } 
data B { name :: Text } 

allora abbiamo appena spostato il problema di definizioni contrastanti tipo di classe:

class Has'name a where 
    name :: a -> String 

class Has'name a where 
    name :: a -> Text 

In linea di principio questo potrebbe essere risolto uno in un modo o nell'altro, ma questa è solo una delle molte difficili proprietà in conflitto tra loro per i record. Quando è stato definito Haskell, è stato deciso che era meglio avere un supporto semplice se limitato piuttosto che cercare di progettare qualcosa di più ambizioso e complicato. Diversi miglioramenti ai record sono stati discussi in vari momenti e ci sono discussioni perenni, ad es. this Haskell Cafe thread. Forse qualcosa sarà risolto per Haskell Prime.

+0

NO! Lo sapevamo già. E lo ha respinto! Quel tipo di "pensare" cerebralmente morto è * esattamente * il problema. Quello che intendeva era avere spazi dei nomi separati per tipo di dati! Proprio come con OOP, dove A.name non è in conflitto con B.name. (nome A) e (nome B) non dovrebbero, per lo stesso identico motivo. Voglio dire che poteva solo internamente desugar «data A {nome :: String}» e «lascia a = A" Betty "nel nome a» in «dati A {a_Name :: String}» e «lascia a = A» Betty "in aName a». Qual è il ritardo? – Evi1M4chine

+4

@ Evi1M4chine: Forse dovresti leggere alcune delle copiose discussioni esistenti sulla questione prima di proporre qualcosa che in realtà non funziona molto bene e poi chiedere ad altre persone di implementarlo. –

+0

@ Evi1M4chine Allora qual è il tipo di 'nome' nel tuo esempio? Deve avere una sorta di tipo principale, altrimenti l'inferenza di tipo in generale si spezzerà in modo orribile. Ora, se non ti interessa troppo l'inferenza del tipo a ritroso, potresti desugar in famiglie aperte di tipo non-iniettivo. – semicolon

4

Molte ragioni, perlopiù secondarie.Uno è il problema sollevato da a better answer, l'overloading solo sul primo argomento non è sufficiente per gestire tutti i casi utili.

Si potrebbe "desugar"

data A { name :: String } 
data B { name :: Text } 

in

class Has'name a b | a -> b where 
    name :: a -> b 

data A { aName :: String } 
instance Has'name A String where 
    name :: aName 

data B { bName :: Text } 
instance Has'name B Text where 
    name :: bName 

ma ciò richiederebbe estensioni GHC (dipendenze funzionali) che non hanno fatto nello standard, ancora. Eviterebbe di usare solo "nome" per la creazione di record, gli aggiornamenti e la corrispondenza del modello (i modelli di visualizzazione potrebbero essere d'aiuto), poiché "nome" non è "solo" una funzione in questi casi. Probabilmente puoi estrarre qualcosa di molto simile con il modello Haskell.

+0

Questo non risolve l'aggiornamento polimorfico o fa gli altri typeclass nella pagina –

+0

Se è necessario eseguire l'aggiornamento polimorfico, è probabile che il "nome" sia ancora definito all'interno di una classe di caratteri, ma assicurarsi che sia di tipo Functor f => (a -> fb) -> s -> ft (o qualcosa "chiudi") in modo che possa essere usato come obiettivo. A quel punto però, in genere avrei errato sul lato con nomi diversi, se lunghi, dei diversi obiettivi, tramite importazioni qualificate. –

0

Il modo migliore che ho trovato è quello di utilizzare un preprocessore per risolvere questo problema decisamente piuttosto stupido.

Haskell e GHC rendono questo facile, perché l'intero parser Haskell è disponibile come una normale libreria. Potresti solo analizzare tutti i file, fare lo schema di ridenominazione (ad es. «Data A {nome :: String}» e «let a = A" Betty "nel nome a» in «data A {a_Name :: String}» e « sia a = A "Betty" in aName a ») a seconda del tipo di dati a cui è applicata la funzione nome, utilizzando il resolver di tipo e scrivendoli per la compilazione.

Ma onestamente, questo dovrebbe essere integrato in GHC. Hai ragione: è sciocco che non sia incluso.

+0

Ma non tutti i tipi sono noti al momento della compilazione, quindi devi preoccuparti di cose come i tipi principali. Qual è il tipo di 'nome' nel tuo esempio? Deve avere UN tipo per farlo funzionare bene con il resto di Haskell in termini di inferenza di tipo e così via. Se 'A' e' B' sono entrambi un'istanza di 'Monoid' per esempio, cosa ti dà' nome Mempty'? – semicolon