In this talk around the 1:20 mark, Edward Kmett menziona la mancanza di "classe backtracking" in Haskell. Si consideri il problema di "set uguaglianza" (in cui l'ordine e la molteplicità sono ignorati) implementati sulle liste:Esiste una soluzione alternativa per la mancanza di backtracking della classe di tipi?
equals :: [a] -> [a] -> Bool
Per la natura delle classi di tipo non posso fornire un confronto inefficiente O (n²) se tutto quello che abbiamo è Eq a
ma confrontare in modo efficiente gli elenchi in O (n log n) se disponiamo di Ord a
.
Ho capito perché una tale struttura sarebbe problematica. Allo stesso tempo, Edward dice che ci sono "trucchi che potresti giocare" menzionando per esempio le famiglie di tipi.
Quindi la mia domanda è, quale sarebbe una soluzione per ottenere lo stesso effetto:
- una (inefficiente) implementazione predefinita è previsto
- se l'utente può fornire alcune informazioni aggiuntive sul tipo, abbiamo "sblocca" un'implementazione più efficiente
Questa soluzione alternativa non deve necessariamente utilizzare le classi di tipi.
Edit: Qualcuno ha suggerito semplicemente fornendo equals
e efficientEquals
come due metodi distinti. In generale sono d'accordo che questo è l'approccio più idiomatico di Haskell. Tuttavia, non sono convinto che ciò sia sempre possibile. Per esempio, che cosa se il metodo equals di cui sopra è a sua volta parte di una classe tipo:
class SetLike s where
equals :: Eq a => s a -> s a -> Bool
Si supponga che questa classe è stata fornita da qualcun altro, quindi non posso semplicemente aggiungere una nuova funzione al typeclass. Ora voglio definire l'istanza per []
. So che lo può sempre fornire un'implementazione di equals
, non importa quali siano i vincoli su a
ma non posso dire alla mia istanza di utilizzare la versione più efficiente se sono disponibili ulteriori informazioni su a
.
Forse potrei avvolgere la lista in un newtype e taggarla con alcune informazioni di tipo aggiuntive?
Questo è piuttosto interessante e non conosco la risposta a questo. Ma una soluzione rapida e sporca sarebbe definire una funzione diversa come 'efficientEquals :: (Ord a) => [a] -> [a] -> Bool' e' equals :: (Eq a) => [a] -> [a] -> Bool'. – Shoe
Per favore chiarisci che stiamo parlando di confrontare gli elenchi _disregarding order_ (e possibilmente anche la molteplicità). Chiaramente '(==)' non richiede O (n^2) sugli elenchi. – chi
IIRC, GHC consente di specificare un '{- # RULE ... equals = efficientOrdEquals # -}' che viene applicato solo se i controlli di tipo RHS. In questo modo, le chiamate che sono noti staticamente per coinvolgere i tipi ordinati otterrebbero la versione efficiente. – chi