Sì. Questo si chiama "dizionario che passa stile". A volte, quando faccio alcune cose particolarmente complicate, ho bisogno di scartare una macchina da scrivere e trasformarla in un dizionario, perché il passaggio del dizionario è più potente 1 , ma spesso piuttosto ingombrante, rendendo il codice concettualmente semplice molto complicato. Uso lo stile del passaggio del dizionario a volte in lingue che non sono Haskell per simulare le macchine da scrivere (ma ho imparato che di solito non è un'idea grandiosa come sembra).
Naturalmente, ogni volta che c'è una differenza nel potere espressivo, c'è un compromesso. Mentre puoi usare una determinata API in più modi se è scritta usando DPS, l'API ottiene più informazioni se non puoi. Un modo in cui questo appare in pratica è in Data.Set
, che si basa sul fatto che esiste un solo Ord
dizionario per tipo. I Set
negozi i suoi elementi ordinati in base al Ord
, e se si crea un set con un dizionario, e poi inserito un elemento con uno diverso, come sarebbe possibile con DPS, si potrebbe rompere Set
's invariante e causare il crash. Questo problema di unicità può essere mitigato usando un fantasma esistenziale tipo per segnare il dizionario, ma, ancora una volta, al costo di un po 'di fastidiosa complessità nell'API. Questo si presenta anche più o meno allo stesso modo Typeable
nell'API.
L'unicità non emerge molto spesso. Ciò che la macchina da scrivere è eccezionale è scrivere codice per te. Per esempio,
catProcs :: (i -> Maybe String) -> (i -> Maybe String) -> (i -> Maybe String)
catProcs f g = f <> g
che accetta due "processori" che accettano un input e potrebbero fornire un output e li concatena, appiattendoli Nothing
, dovrebbero essere scritti in DPS in questo modo:
catProcs f g = (<>) (funcSemi (maybeSemi listSemi)) f g
In sostanza, abbiamo dovuto precisare il tipo in cui lo stiamo usando di nuovo, anche se lo abbiamo già scritto nella firma del tipo, e anche questo era ridondante perché il compilatore conosce già tutti i tipi. Poiché esiste solo un modo per costruire un dato Semigroup
in un tipo, il compilatore può farlo per te. Questo ha un effetto di tipo "interesse composto" quando inizi a definire molte istanze parametriche e ad usare la struttura dei tuoi tipi per calcolare per te, come nei Data.Functor.*
combinatori, e viene usato con grande effetto con deriving via
cui puoi essenzialmente ottenere tutto struttura algebrica "standard" del tuo tipo scritta per te.
E non farmi nemmeno iniziare su MPTC e fundeps, che restituiscono informazioni al controllo dei caratteri e all'inferenza. Non ho mai provato a convertire una cosa del genere in DPS - sospetto che comporterebbe il passaggio di molte prove di uguaglianza di tipo - ma in ogni caso sono sicuro che sarebbe molto più lavoro per il mio cervello di quanto non mi sentirei a mio agio con.
-
1 U lla si utilizza reflection
in questo caso diventano equivalente al potere - ma reflection
può anche essere ingombrante per l'uso.