Inizierò introducendo un problema concreto (StackOverflow è così). Dire si definisce un tipo semplicePerché le istanze corrispondono solo alle loro teste?
data T a = T a
Questo tipo è un Functor
, Applicative
e Monad
. Ignorando la derivazione automatica, per ottenere quelle istanze devi scriverle singolarmente, anche se implica Applicative
, che implica Functor
. Più di questo, potrei definire una classe come questa
class Wrapper f where
wrap :: a -> f a
unwrap :: f a -> a
Questa è una condizione piuttosto forte e implica sicuramente Monad
, ma non riesco a scrivere
instance Wrapper f => Monad f where
return = wrap
fa >>= f = f $ unwrap fa
perché questo, per qualche ragione , significa "tutto è un f
(f
), solo se è un Wrapper
", invece di "tutto ciò che è un Wrapper
è un Monad
".
Analogamente non è possibile definire le istanze Monad a => Applicative a
e Applicative a => Functor a
.
Un'altra cosa che non è possibile fare (che è probabilmente solo correlata, non lo so davvero) è che una classe sia una superclasse di un'altra e fornire un'implementazione predefinita della sottoclasse. Certo, è bello che sia class Applicative a => Monad a
, ma è molto meno bello che io debba ancora definire l'istanza Applicative
prima di poter definire lo Monad
.
Questo non è un rant. Ho scritto molto perché altrimenti questo sarebbe stato rapidamente contrassegnato come "troppo ampio" o "poco chiaro". La domanda si riduce al titolo. So (almeno sono abbastanza sicuro) che c'è qualche ragione teorica per questo, quindi mi chiedo quali sono esattamente i vantaggi qui.
Come domanda secondaria, vorrei chiedere se ci sono alternative valide che mantengono tutti (o la maggior parte) di questi vantaggi, ma consentono ciò che ho scritto.
Aggiunta: Ho il sospetto che una delle risposte potrebbe essere qualcosa del tipo "Che cosa succede se il mio tipo è un Wrapper
, ma io non voglio usare l'istanza Monad
che questo comporta?". A questo chiedo, perché il compilatore non ha potuto scegliere quello più specifico? Se c'è uno instance Monad MyType
, sicuramente è più specifico di instance Wrapper a => Monad a
.
'unwrap' descrive un comportamento che nessuno dei metodi di' Monad' implementa; se così non fosse, 'unwrap' ti permetterebbe di estrarre un' a' da una computazione 'IO a' (come' unsafePerformIO' fa), il che vanificherebbe lo scopo di avere una monade 'IO' nella prima posto. Una classe può essere solo meno potente/espressiva delle sue sottoclassi. – Jubobs
Quale sarà la definizione di 'unwrap' per' Nothing' quando si definisce l'istanza per il tipo 'Maybe'? – Sibi
@Sibi Semplice, non c'è istanza per Maybe. Non penso abbia sostenuto che ci fosse. Inoltre, dubito che Wrapper implichi Monad senza alcuna legge (diversa da quelle gratuite). – Cubic