2011-08-28 7 views
24

La funzione uncurry funziona solo per le funzioni di assunzione di due argomenti:Haskell ha funzioni/tuple variadiche?

uncurry :: (a -> b -> c) -> (a, b) -> c 

Se voglio uncurry funzioni con un numero arbitrario di argomenti, ho potuto solo scrivere funzioni distinte:

uncurry2 f (a, b)   = f a b 
uncurry3 f (a, b, c)  = f a b c 
uncurry4 f (a, b, c, d) = f a b c d 
uncurry5 f (a, b, c, d, e) = f a b c d e 

Ma questo ottiene noioso rapidamente. C'è un modo per generalizzare questo, quindi devo solo scrivere una funzione?

+0

Non in questo modo, ma in ogni caso, perché vuoi farlo? Nella mia esperienza ci sono pochissimi casi in cui hai bisogno di qualcosa di più che uncurry2 –

+7

@Paul: Nessun motivo specifico, ma non appena vedo qualche tipo di schema ripetuto, mi chiedo come posso generalizzare e astrarre da esso. – fredoverflow

risposta

23

Provare dal pacchetto tuple. Come tutte le forme di sovraccarico, è implementato usando classi di tipi. In questo caso, ortografa manualmente le istanze fino a 15 tuple, che dovrebbero essere più che sufficienti.

Le funzioni variabili sono anche possibili utilizzando le classi di tipi. Un esempio di questo è Text.Printf. In questo caso, è fatto per induzione strutturale sul tipo di funzione. Semplificato, funziona così:

class Foo t 

instance Foo (IO a) 
instance Foo b => Foo (a -> b) 

foo :: Foo 

Non dovrebbe essere difficile capire che foo possibile creare un'istanza ai tipi IO a, a -> IO b, a -> b -> IO c e così via. QuickCheck usa anche questa tecnica.

induzione strutturale non funziona su tuple, anche se, come -tuple n è del tutto estraneo a un -tuple n + 1, quindi è per questo che le istanze devono essere precisato manualmente.

+5

In modo divertente, la fonte di 'Data.Tuple.Curry' indica che le diverse istanze sono state effettivamente generate da un programma separato, e quindi presumibilmente incollate come se fossero state digitate a mano. –

2

Non esiste un modo semplice per scrivere una singola definizione di uncurry che funzioni per diversi numeri di argomenti.

Tuttavia, è possibile utilizzare Template Haskell per generare le molte varianti diverse che altrimenti si dovrebbe scrivere a mano.

+0

Puoi scrivere un n-ary 'uncurry' con Daniel Fridlender e il pattern di Mia Indrika per le funzioni familiari di arity. L'esempio di 'zipWithN' è riportato nel documento" Abbiamo bisogno di tipi dipendenti? ". –

+0

"No way" era forse un po 'audace. Ho cambiato la mia risposta a "nessun modo semplice". –

+0

@stephentetley Questa implementazione è stata implementata in pacchetto ovunque? – CMCDragonkai

11

Trovare i modi per simulare questo tipo di cose utilizzando i trucchi del sistema di tipo eccessivo è uno dei miei hobby, quindi fidati di me quando dico che il risultato è piuttosto brutto. In particolare, si noti che le tuple non sono definite in modo ricorsivo, quindi non esiste un modo reale di astrarre direttamente su di esse; per quanto riguarda il sistema di tipi di Haskell, ogni dimensione di tuple è completamente distinta.

Qualsiasi approccio praticabile per lavorare direttamente con le tuple richiederà quindi la generazione del codice - utilizzando TH o uno strumento esterno come nel pacchetto tuple.

Per simulare l'errore senza utilizzare il codice generato, è necessario ricorrere innanzitutto a definizioni ricorsive, in genere coppie nidificate con valore "nullo" per contrassegnare la fine, (,) e () o qualcosa di simile. Si può notare che questo è simile alla definizione di elenchi in termini di (:) e [] - e infatti, le tuple di questo tipo definite in modo ricorsivo possono essere viste come strutture di dati di tipo livello (un elenco di tipi) o come liste eterogenee (ad esempio, HList funziona in questo modo).

Gli aspetti negativi includono, ma non sono limitati a, il fatto che in realtà utilizza cose costruito in questo modo può essere più imbarazzante di quello che vale, il codice per implementare i trucchi del sistema tipo di solito è sconcertante e completamente non-portatile, e il risultato finale non è necessariamente equivalente comunque - ci sono molteplici differenze non banali tra (a, (b, (c,()))) e (a, b, c), per esempio.

Se vuoi vedere quanto diventa orribile puoi guardare the stuff I have on GitHub, in particolare i bit here.