2016-12-10 8 views
2

Сегодня на «приключениях в функциональном программировании с объективами» наш герой пытается определить призму над элементом списка, который может или не может существовать.Призмы над элементом существующего списка

Это немного сложно объяснить, поэтому, чтобы избежать проблемы X, Y, я дам фактический прецедент во всей красе.

Я пишу текстовый редактор в Haskell под названием Rasa, вся идея в том, что он чрезвычайно расширяемый, и это означает, что большинство функций написано как расширения. Поскольку это основной принцип, расширения ТАКЖЕ зависят от других расширений, поэтому мне нужен способ централизованного хранения их состояния, чтобы все расширения в зависимости от расширения могли получить доступ к его текущему «состоянию расширения». Конечно, типы этих состояний не известны ядру редактора, поэтому на данный момент я сохраняю список значений Dynamic. Когда расширение хранит состояние он преобразуется в динамическом, то он может быть извлечен позже через призму так:

data Store = Store 
    { _event :: [Event] 
    , _editor :: E.Editor 
    , _extState :: [Dynamic] 
    } 

ext :: Typeable a => Traversal' Store a 
ext = extState.traverse._Dynamic 

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

BTW, я бы хотел, чтобы в любой момент в списке было точно 1 экземпляр объекта состояния Extension.

Таким образом, получение и настройка на самом деле работают отлично IFF, в списке уже есть нужный тип, мой вопрос: как я могу сделать версию этого обхода таким образом, что если значение соответствующего типа находится в что он заменит его (и будет работать как ожидалось), но это добавит значение в список, если обход будет SET, и в списке нет соответствующего элемента. Я понимаю, что Traversal не должен изменять количество элементов, которые они пересекают, так что, возможно, это должно быть призмой или объективом над самим списком?

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

Что касается вещей, которые я принял взглянуть на уже, я смотрел на prefixed и _Cons в качестве возможных способов сделать это, но я «Я просто не совсем уверен, как подключить его. Может быть, я полностью лаем по неправильному дереву.

Я мог бы добавить значение по умолчанию в конец обхода каким-либо образом, но я не хочу требовать моноида или по умолчанию, поэтому я не могу вызвать в воображении элементы из ниоткуда (и я хочу сделать это только тогда, когда SETTING).

Я также открыт для дискуссий о том, действительно ли это правильный способ сохранить это состояние, но это самые изящные решения, которые я нашел до сих пор (хотя я знаю, что при прогнозировании во время выполнения - оптимальный). Я просмотрел тип Vault, но прохождение ключей вокруг не очень хорошо работало, когда я его пробовал (и я думаю, что он имеет похожие проблемы с производительностью при произнесении типов).

Cheers! Спасибо за прочтение.

ответ

1

Я думаю, что список расширений не является правильным решением для вас. Я бы добавил что-то вроде _extState :: Map TypeRep Ext где data Ext = forall a. Ext a. Тогда я бы добавил объектив:

ext :: forall a . Typeable a => Lens' Store (Maybe a) 
ext = _extState . at (typeRep (Proxy :: Proxy a)) . mapping coerce 
    where 
    coerce = iso (\(Ext x) -> unsafeCoerce x) Ext 

Этот объектив делает как at. Таким образом, вы можете просто получить/установить свои расширения.

Но есть одно ограничение, все расширения должны быть разных типов.

+0

Похоже, это может сработать! Могли бы вы объяснить (Proxy :: Proxy a)? Создает ли TypeRep для предоставленного типа? Кроме того, мне нужно «принуждение» и «Ext», или я могу сделать «Map TypeRep Dynamic»? или быстрее использует Ext с экзистенциальной квантификацией? (Я полагаю, что unsafeCoerce безопасна, поскольку я знаю, какой тип хранится в typeref). Просто интересно, почему вы сделали выбор, чтобы пойти с «Ext a». Я буду играть с этим, как только у меня будет время, и дайте знать, подходит ли это! –

+0

P.S. Я только хотел, чтобы в любом случае был один из типов Extension, поэтому на самом деле это функция, а не ограничение: D –

+1

Да, это так. Это новый способ получить TypeRep для чего-то. Он был добавлен к базе с 4.7.0.0. Для использования с '(Proxy :: Proxy a)' вам нужно использовать '-XScopedTypeVariables' – freestyle