Come si adattano le classi di tipi in questo modello?
La risposta breve è: non lo fanno.
Ogni volta che si introducono coercizioni, classi di tipi o altri meccanismi per il polimorfismo ad hoc in un linguaggio, il principale problema di progettazione che si incontra è la coerenza .
Fondamentalmente, è necessario assicurarsi che la risoluzione della typeclass sia deterministica, in modo che un programma ben tipizzato abbia una singola interpretazione. Ad esempio, se potessi fornire più istanze per lo stesso tipo nello stesso ambito, potresti potenzialmente scrivere programmi ambigui come questo:
class Blah a where
blah : a -> String
instance Blah T where
blah _ = "Hello"
instance Blah T where
blah _ = "Goodbye"
v :: T = ...
main :: IO ()
main = print (blah v) -- does this print "Hello" or "Goodbye"?
A seconda della scelta dell'istanza effettuata dal compilatore, blah v
potrebbe essere uguale a "Hello"
o "Goodbye"
. Pertanto, il significato di un programma non sarebbe completamente determinato dalla sintassi del programma, ma piuttosto potrebbe essere influenzato da scelte arbitrarie fatte dal compilatore.
La soluzione di Haskell a questo problema è richiedere che ogni tipo abbia al massimo un'istanza per ogni classe di caratteri. Per garantire ciò, consente dichiarazioni di istanza solo al livello più alto e rende inoltre visibili tutte le dichiarazioni a livello globale. In questo modo, il compilatore può sempre segnalare un errore se viene fatta una dichiarazione di istanza ambigua.
Tuttavia, rendere le dichiarazioni visibili a livello globale interrompe la compositività della semantica. Quello che puoi fare per recuperare è dare una semantica di elaborazione per il linguaggio di programmazione, cioè puoi mostrare come tradurre i programmi Haskell in un linguaggio più educato e più compositivo.
Questo in realtà ti dà un modo per compilare anche le macchine da scrivere - di solito viene chiamata "traduzione delle prove" o "trasformazione del passaggio del dizionario" nei circoli di Haskell ed è una delle prime fasi della maggior parte dei compilatori di Haskell.
Le typeclass sono anche un buon esempio di come il design del linguaggio di programmazione differisce dalla pura teoria dei tipi. Le macchine da scrivere sono una caratteristica del linguaggio davvero eccezionale, ma sono abbastanza maleducate dal punto di vista della teoria delle prove. (Ecco perché Agda non ha affatto le macchine da scrivere e perché Coq le rende parte della sua infrastruttura euristica di inferenza.)