2013-08-24 13 views
20

Tutto quello che so è che uno funziona e l'altro no.Qual è la differenza tra `ix` e` at` nella libreria Lens di Haskell

Contesto: ho una struttura di dati che contiene un FData.Map.Map k S ad un'altra struttura di dati S. Il mio obiettivo era quello di costruire un Lens che dato un F e k descrivesse un campo in S.

La difficoltà è che il tasto k potrebbe non essere presente nella mappa. Va bene, la funzione può avvolgere il suo ritorno in Forse. Tuttavia, non è stato possibile propagare un obiettivo attraverso un Forse usando at. Dopo aver letto un sacco di risposte Stack Overflow, mi sono imbattuto in this one.

Risulta che la sostituzione at con ix risolto il problema di tipo se anche ho sostituito con (^.)(^?).

Domanda: sembra at e ix fare la stessa cosa, almeno per quanto riguarda Map. Entrambi prendono una chiave e danno una "lente" al valore di quella chiave. Tuttavia, ix sembra funzionare correttamente con l'operatore di composizione delle funzioni (.). Qual è la differenza tra i due?


fuori tema Rant:

mi piace operatori infissi tanto quanto il prossimo ragazzo, ma il pacchetto Control.Lens sembra essere andato un po 'fuori bordo. Per un nuovo utente che ha alcuni nomi inglesi e una chiave da qualche parte abbasserebbe la curva di apprendimento. A causa dell'enorme numero di classi wrapper utilizzate nella libreria Lens, è particolarmente difficile scavare attraverso le firme del tipo se non si sa già cosa sta succedendo. Il mio codice sta iniziando a sembrare Perl per l'amor del cielo.

+0

È possibile utilizzare 'lens' senza un singolo operatore infisso. Quasi ogni operatore è solo un alias conveniente per un nome non operatore - non è necessario utilizzarli (e spesso non lo faccio). – shachaf

+2

quello che non puoi fare è cercare di leggere e capire il codice che altri hanno scritto, diciamo, nella cartella di esempio delle librerie. Il processo di copia di un codice e di battitura su di esso in GHCi è molto più difficile in Lens, quindi nella maggior parte delle altre librerie. –

+0

@shachaf che non è vero: alcuni operatori altamente utili non esistono come parole, ovvero '% =' (non così male dato che è usato nei blocchi 'do') e'? ~ '(Piuttosto male, dato che è veramente utile in puro codice, che richiede il passaggio da '$' a '&' se non si vuole caricare su parens). – spopejoy

risposta

19

Che at e ix sono diversi è già notevole se si guardano le istanze disponibili per le classi che contengono queste funzioni:

  • istanze di At: Map, IntMap, HashMap
  • istanze di Ixed: [ a], Map, ByteString, testo, e molto altro ancora

Tutte le istanze se At sono anche un'istanza di Ix, ma non tutti i le istanze di Ix sono anche un'istanza di At.

Quindi qual è la differenza tra loro? At è per i contenitori che consentono l'inserimento di chiavi che non sono presenti nel contenitore. Questo è ovviamente possibile per una mappa, ma non per es. per una lista. Per essere ancora in grado di indicizzare un elenco e modificare gli elementi presenti, lo Ix non consente la creazione di nuovi elementi, ma semplicemente "non fa nulla" quando si tenta di scrivere su una chiave che non è presente.

>>> Data.Map.fromList [('a', 1)] & at 'b' .~ Just 4 
fromList [('a',1),('b',4)] -- Inserts value 
>>> Data.Map.fromList [('a', 1)] & ix 'b' .~ 4 
fromList [('a',1)]   -- Does nothing because key is not present 

(V'è inoltre un collegamento per a .~ Just b, a ?~ b)

Tecnicamente, questa differenza deriva dal fatto che è un ixTraversal che at è un Lens. E poiché at è un obiettivo che "restituisce" un qualcosa del genere, non è possibile comporlo con un obiettivo che richiede solo un "qualcosa". ix è un attraversamento con valori 0 o 1, quindi puoi comporre ix come qualsiasi altro attraversamento (proprio come puoi scrivere traverse . traverse). (^?) prende solo il primo valore (testa) di quella traversata.

È sempre possibile ricavare ix da at:

ixAt = at . traverse 

La stessa definizione is already in lens, tranne che sta utilizzando (<.) per la composizione di mantenere l'indice da presso. (at e ix sono entrambi lenti/traverse indicizzati).

Off-topic: La maggior parte degli operatori di lenti hanno anche nomi infissa, è possibile trovare un (incompleta) tavolo al: https://github.com/ekmett/lens/wiki/Operators

+2

Penso che "abbastanza diverso" la stia esagerando - "ix k = at k. traverse' è una legge, quando entrambi sono implementati. 'at' fornisce strettamente più funzionalità di' ix' (ma 'ix' è implementato per più tipi). – shachaf

+0

@shachaf Hai ragione. Ho modificato la risposta. – bennofs

Problemi correlati