2013-06-09 10 views
12

Mam kilka typów danych wzdłuż liniiJak mogę użyć Control.Lens do aktualizacji i-tego elementu listy?

data Outer = Outer { _list :: [ Inner ] } 
data Inner = Inner { _bool :: Bool } 

pomocą Control.Lens mogę uzyskać dostęp do _bool z ith wewnętrzna (wewnątrz monady 'państwo Outer') jak to

boolValue <- gets (^. list . to (!! i) . inner) 

I chciałby być także w stanie zaktualizować tę wartość z czymś jak

list ^. (to (!! i)) ^. inner %= True 

jednak (wg mojego zrozumienia), funkcja „na” tworzy tylko getter, a nie prawdziwy len s może być używany jako getter lub seter.

Jak więc przekonwertować (!! i) na obiektyw, który pozwoli mi zaktualizować to pole?

Odpowiedz

15

nie można włączyć (!!) * do niczego podobnego obiektywu innego niż Getter - ale istnieje funkcja robić tego typu rzeczy: ix, dostępu do th na indeksach. W rzeczywistości jest to Traversal, a nie Lens - co oznacza tylko, że może zawieść (jeśli indeks jest poza zakresem) - ale tak długo, jak indeks znajduje się na liście, zadziała.

Jest jednak inny problem - (^.) to również operator, który służy wyłącznie do uzyskiwania wartości. Jest niezgodny z np. (%=), który jako pierwszy argument bierze soczewki. Oraz: (%=) służy do mapowania funkcji na istniejącą wartość; jeśli chcesz tylko ustawić, możesz użyć (.=). Więc prawdopodobnie chcesz coś takiego:

list . ix i . inner .= True 

* Istnieje rzeczywiście jest funkcją, która może to zrobić - to się nazywa upon - ale korzysta wspaniały złego czarnej magii i nie należy go używać, przynajmniej nie dla to (i prawdopodobnie nie dla żadnego prawdziwego kodu).

+0

czy mógłbyś wyjaśnić jaka jest różnica między 'ix' a' elementem'? Poszedłem z "elementem" Gabriela, ponieważ wydaje się prostsze, biorąc "Int" zamiast "Index". Z dokumentów, które łączyłeś, wydaje się, że "ix" jest bardziej ogólny w tym, na co pozwala - czy jest on bardziej ogólny? – ajp

+0

Nie myślałem o "elemencie". Nie jest tak, że jeden jest bardziej ogólny niż drugi ... 'ix' jest typem klasy, który ma kilka instancji do indeksowania z konkretnymi typami indeksów (na przykład listy z' Int', 'Map's with ich typ indeksu, funkcjonuje w ich domenie). 'element' pobiera dowolny typ" Traversable "i zlicza od lewej do prawej z indeksem' Int'. W tym przypadku spotykają się ze sobą. – shachaf

+0

Nie testowałem tego, ale przypuszczam, że 'ix' jest nieco bardziej wydajny w tym konkretnym przypadku (głównie dlatego, że nie wymyśliłem sposobu, aby' Indexing' wygenerował dobry kod ... :-(Ktoś inny jest mile widziany, aby spróbować) .Jest to również nieco klasy ad-hoc. – shachaf

8

Zastosowanie element, który jest Traversal do podanej listy element:

list . element i . inner %= True :: Outer -> Outer 

Jeśli chcesz uzyskać element listy musi to zrobić za pomocą Maybe ponieważ element lista może nie być tam:

myList :: Outer 

myList ^? list . element i . inner :: Maybe Bool 
+0

Zauważ, że '% =' daje działanie 'State' do modyfikacji stanu, tzn.' ... :: State Outer() '. (Ponadto zakładam, że pytanie oznaczało '. =' Tutaj.) – shachaf

+0

Tak, mam na myśli '% ~', ale myślę, że i tak twoja odpowiedź jest lepsza. –

Powiązane problemy