Durante la scrittura di programmi più grandi in Haskell mi trovo a trovarmi di fronte a un problema comune. Mi trovo spesso a desiderare più tipi distinti che condividono una rappresentazione interna e diverse operazioni di base.Gestione di più tipi con la stessa rappresentazione interna e numero minimo di caratteri?
Ci sono due approcci relativamente ovvi per risolvere questo problema.
Uno sta utilizzando una classe di tipo e l'estensione GeneralizedNewtypeDeriving
. Inserire una logica sufficiente in una classe di tipo per supportare le operazioni condivise che il caso d'uso desidera. Creare un tipo con la rappresentazione desiderata e creare un'istanza della classe del tipo per quel tipo. Quindi, per ogni caso d'uso, crea dei wrapper per esso con newtype e ricava la classe comune.
L'altro è dichiarare il tipo con una variabile di tipo fantasma, quindi utilizzare EmptyDataDecls
per creare tipi distinti per ogni caso di utilizzo diverso.
La mia preoccupazione principale non è mescolare valori che condividono la rappresentazione e le operazioni interne, ma hanno significati diversi nel mio codice. Entrambi gli approcci risolvono questo problema, ma si sentono decisamente goffi. La mia seconda preoccupazione è la riduzione della quantità di piastra di riscaldamento richiesta, e entrambi gli approcci funzionano abbastanza bene.
Quali sono i vantaggi e gli svantaggi di ciascun approccio? Esiste una tecnica che si avvicini a fare ciò che voglio, fornendo sicurezza di tipo senza codice boilerplate?
Se la memoria mi serve, 'dati Foo a = Foo a',' dati Foo ab = Foo a', e 'newtype Bar a = Bar (Foo a)' (con il primo 'Foo') dovrebbero essere tutti compilati allo stesso rappresentazione runtime, quindi trovare una differenza non banale nelle prestazioni sarebbe alquanto inaspettata. –
@camccann La bellezza di ghc-core e Criterion è una prova empirica per integrare la memoria! :) Penso che la domanda sulle prestazioni abbia più a che fare con il fatto che le operazioni provenienti da una classe influiscano o meno sulle loro prestazioni rispetto alla rappresentazione runtime del valore stesso. Le funzioni polimorfiche vanno da '(Generale a, Generale b) => a -> b -> Int' a' Generale2 a -> Generale2 b -> Int'. – Anthony