2012-04-05 4 views
5

Sto cercando di utilizzare le comprensione di monad per rappresentare query SQL e generare l'SQL appropriato. A prima vista, questo non è un problema, sembra una buona misura. Ma devo limitare i tipi, che possono formare la monade solo per i prodotti, senza somme, e non riesco a pensare a un modo per porre tali vincoli.Come posso vincolare un tipo (tipo?) Solo ai tipi di prodotto

Desidero utilizzare il controllo di tipo per garantire che possano essere utilizzati solo i tipi rappresentabili in SQL.

Potrei, suppongo, utilizzare template haskell per ricavare le istanze corrette e rifiutare di derivare se il tipo non si adatta, ma preferirei farlo a livello di testo. Meno possibilità per me di introdurre bug dovuti alla mia ignoranza.

Come posso fare questo? Se sì, potresti raccomandare alcuni esempi di lettura e/o codice per favore.

Modifica: Grazie, ho alcuni buoni percorsi da seguire, che richiedono più lettura :) E qui arriva un lungo weekend.

risposta

8

Sfortunatamente, questo non è realmente possibile: uno Monad deve essere completamente polimorfico. È lo stesso motivo per cui non è possibile effettuare una monade con Set (il vincolo Ord).

Se è possibile gestire solo il tipo di risultato che soddisfa il vincolo, è possibile avere runSQL :: (Product a) => SQL a -> IO a o simile. In tal caso, derivare semplicemente le istanze appropriate con Template Haskell è la strada da seguire, o in alternativa, utilizzando il nuovo GHC Generics; haskell semplice non ha modo di determinare se un tipo è composto solo da prodotti.

Ma ho il sospetto che sia necessario accedere all'intera struttura del calcolo monadico, per tradurlo in SQL. Sfortunatamente, le monadi non sono molto ben attrezzate per questo, dal momento che sono collegate con funzioni arbitrarie di Haskell, che non puoi "guardare dentro". Arrows sono più vicini, e ti permettono di fare più analisi statiche, ma hanno ancora quel fastidioso arr che, di nuovo, ti permette di intrufolarti nelle funzioni arbitrarie di Haskell.

L'opzione più valida per fare qualcosa di simile è probabilmente scrivere uno splicer Template Haskell per analizzare la sintassi che si desidera; puoi dire $(sql [| [ (a,b) | a <- table1, b <- table2 |]) e avere sql tradurre AST nel corrispondente SQL in fase di compilazione Se la sintassi è troppo brutta, scrivi una quasiquoter con haskell-src-meta, che assomiglierebbe a [sql| (a, b) | a <- table1, b <- table2 |].

Potresti anche essere interessato all'estensione generalised arrows, anche se probabilmente è eccessivo (e troppo sperimentale) per i tuoi scopi.

+0

Grazie @ehird, questo mi dà spunti di riflessione. E, sì, penso che limitare il tipo di risultato potrebbe essere sufficiente. La classe Product rappresenta una riga SQL, che è il risultato di una query SQL SELECT, e per le query senza join questo dovrebbe essere sufficiente. Sto provando a lavorare su un mini DSL e la mia speranza iniziale era di raggiungere uno stadio, in cui il risultato di _runSQL_ è una stringa di query accettabile. Quindi qualcosa sulla falsariga di '(Product a) => runSQL :: SQL a -> a'. Lasciare l'esecuzione della query a qualsiasi cosa faccia un back-end db. – dikini

+0

Mi spiace, in quanto sopra la funzione def dovrebbe leggere 'runSQL :: (Product a) => SQL a -> AST a' per alcuni' AST a' – dikini

+0

Le Monade non devono più essere completamente polimorfiche, con [tipi di vincoli ] (http://blog.omega-prime.co.uk/?p=127), vero? Non so se questi sono applicabili al problema dell'OP, però. – leftaroundabout

Problemi correlati