2013-08-05 9 views
6

Sto usando lente biblioteca di Edward Kmett per la prima volta, e la ricerca è piuttosto bello, ma mi sono imbattuto in un intoppo ...Come evitare l'uso di obiettivi con tipi esistenti?

La domanda a [1] spiega che quantificatori esistenziali interrompono makeLenses. Preferisco davvero usare un esistenziale con le lenti in qualche modo.

Come sfondo, ho la classe:

class (TextShow file, Eq file, Ord file, Typeable file) => File file where 
    fromAnyFile :: AnyFile -> Maybe file 
    fileType :: Simple Lens file FileType 
    path :: Simple Lens file Text.Text 
    provenance :: Simple Lens file Provenance 

Per la domanda attuale, voglio avere il tipo:

data AnyFile = forall file . File file => AnyFile { _anyFileAnyFile :: File } 

E io voglio essere in grado di scrivere qualcosa sulla falsariga di:

Questo non funziona, per la ragione spiegata in [1]. Se chiedo GHC per il debug di informazioni da parte di compilazione con -ddump-splices, ottengo:

Haskell/Main.hs:1:1: Splicing declarations 
    makeLenses ''AnyFile ======> Haskell/Main.hs:59:1-20 

La giunzione stessa è vuota, che indica a me che non le dichiarazioni sono prodotti da esso. Questa parte mi aspetto e capisco ora che ho letto [1].

Quello che mi piacerebbe sapere è come posso fare questo - cosa potrei fare per aggirare il problema? Cosa potrei fare per evitare di nuotare controcorrente su questo? Mi piacerebbe essere in grado di accedere a qualsiasi parte delle mie strutture attraverso un percorso di lenti composte, ma poiché ho campi in altri tipi con tipi come Set AnyFile, non posso farlo a meno che non riesca ad accedere al contenuto di AnyFile con un obiettivo.

[1] Existential quantifier silently disrupts Template Haskell (makeLenses). Why?

+0

Solo per chi si chiede, quello che ho fatto è stato utilizzare il suggerimento di seguito; la definizione doveva essere 'lens (\ (AnyFile file) -> file) (valore \ _ -> AnyFile value)'. –

risposta

7

Nel peggiore dei casi, si può sempre applicare le lenti da soli senza fare affidamento su Template Haskell a tutti.

Ad esempio, dato un getter e una funzione setter per il vostro tipo, è possibile creare una lente utilizzando la funzione lens:

lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b 

Credo che questo potrebbe non essere l'opzione più performante, ma è certamente il più facile.

non so come fare per il vostro caso (o con i tipi esistenziali in generale), ma ecco un esempio banale con un record:

data Foo = Foo { _field :: Int } 
foo = lens _field (\ foo new -> foo { _field = new }) 

Speriamo che questo illustra l'idea abbastanza bene da applicare al tuo codice.

+0

Hmm ... Non sapevo nulla della funzione dell'obiettivo. Grazie! I documenti sono piuttosto grandi. :) Sto cercando di far funzionare qualcosa ora, e accetterò la risposta se lo fa. :) –

+1

Il tipo di obiettivo, almeno nella mia versione, sembra essere 'Functor f => (s -> a) -> (s -> b -> t) -> (a -> fb) -> s -> ft'. Va bene. Sembra una specie di continuation-passing thing, posso probabilmente capirlo ... –

+0

Oh capisco. Destra. Ho solo bisogno dei primi due parametri perché i restanti fanno parte dell'obiettivo stesso ... Forse i doc possono spiegare che cosa è il 's' per me. –