2015-03-25 2 views
3

Я ищу способ ссылаться на элемент вектора с использованием библиотеки линз ...Доступ к элементу вектора по индексу с помощью линзы

Позвольте мне объяснить, что я пытаюсь достичь, используя упрощенный пример моего кода.

Я работаю в этой стопке монада трансформатора (где StateT находится в центре внимания, все остальное не важно)

newtype MyType a = MyType (StateT MyState (ExceptT String IO) a) 

MyState имеет много полей, но один из них является вектор клиентов который является типом данных я определил:

data MyState = MyState { ... 
         , _clients :: V.Vector ClientT 
         } 

Всякий раз, когда мне нужно получить доступ к одному из моих клиентов, я, как правило, сделать это следующим образом:

import Control.Lens (use) 

c <- use clients 
let neededClient = c V.! someIndex 
... -- calculate something, update client if needed 
clients %= (V.// [(someIndex, updatedClient)]) 

Теперь вот что я ищу: мне бы хотелось, чтобы моя функция получала «ссылку» на интересующий меня клиент и извлекала его из State, обновляя его, если это необходимо).

Для того, чтобы прояснить, что я имею в виду вот фрагмент (который не компилируется даже в псевдо-код):

... 
myFunction (clients.ix 0) 
... 

myFunction clientLens = do 
    c <- use clientLens -- I would like to access a client in the vector 
    ... -- calculate stuff 
    clientLens .= updatedClient 

В принципе, я хотел бы перейти к MYFUNCTION что-то из библиотеки объектива (I не знаю, что я здесь передаю ... Объектив? Обход? Что-то другое?), который позволит мне указать на конкретный элемент в векторе, который хранится в моем состоянии. Возможно ли это? В настоящее время при использовании «clients.ix 0» я получаю сообщение об ошибке, что мой ClientT не является экземпляром Monoid.

Это очень глупая версия того, что у меня есть. Чтобы ответить на вопрос «зачем мне это так», требуется гораздо больше объяснений. Мне интересно, можно ли передать эту «ссылку», которая укажет на некоторый элемент в моем векторе, который хранится в состоянии.

ответ

3

clients.ix 0 - это обход. В частности, обходы являются сеттеры, поэтому установка и изменение должно работать нормально:

clients.ix 0 .= updatedClient 

Ваша проблема с use. Поскольку обход не обязательно содержит ровно одно значение, когда вы совершаете обход (или используете для него какую-то другую функцию геттера), он объединяет все значения, предполагая, что они имеют тип Monoid.

В частности,

use (clients.ix n) 

хотел бы вернуть mempty если n находится вне границ.

Вместо этого можно использовать функцию preuse, который отбрасывает все, кроме первого цели обхода (или в более общем плане, складка), и оборачивает его в Maybe. Например.

Just c <- preuse (clients.ix n) 

Примечания это даст ошибку матча шаблона, если n находится вне границ, так как preuse возвращается Nothing тогда.