È lo stesso motivo per cui abbiamo map
e fmap
. Messaggi di errore / usabilità per i neofiti.
Sarebbe potentissimo confondere per molti un nuovo programmatore per scrivere
myLength = subtract 1 . genericLength
x = myLength [1, 2, 3] :: Int
y = myLength [1, 2, 3] :: Integer
e ricevi un errore lamentando la limitazione del monomorfismo. Inoltre quale preferisci
Couldn't match expected type "Integer" with type "Bool"
o
No instance for Num Bool arising from usage ....
È semplicemente una questione di usabilità.
Inoltre, in molti casi finisci sempre con il default, ad esempio con la composizione delle funzioni
evenElems = even . subtract 1 . genericLength
vs
default ()
evenElems = even . genericLength -- Error ambiguous type variable.
Qui vogliamo solo che GHC "scelga" un tipo casuale di Num
e davvero non ci interessa quale (è Integer
IIRC).
Se tutto fosse completamente generico, le regole di default si sarebbero um..nomose. Dal momento che al momento ci sono solo 4 cose che sono automaticamente disponibili come predefinite e nessun modo per impostare qualcosa di granuloso (per funzione).
Per quanto riguarda l'efficienza, typeclass significa trascinare potenzialmente trascinando il dizionario di typeclass e il default a Integer
che è molto più costoso di Int
.
Ci sono preludi alternativi (penso che il preludio di classe sia non mantenuto, ma interessante) che cerchi di essere il più generico possibile. Usarli è semplice come
{- LANGUAGE NoImplicitPrelude #-}
import My.Super.Awesome.Prelude