воскресенье, 29 марта 2015 г.

[prog.trolling] undo/redo операции на основе иммутабельных списков, говорите? ;)

Добрый человек указал вчера на статью "ФП в браузере". Статья хорошая. Читается легко, картинки классные. Если раньше не приходилось читать про достоинства ФП, то имеет смысл потратить время.

Однако, любители функциональщины так сильно напирают на борьбу с изменяемым состоянием, что это начинает напоминать афоризм про борьбу с перхотью посредством отсечения головы :) Даже приведенный в статье пример организации списка undo/redo-операций посредством иммутабельных списков заставил вспомить свой прошлый опыт. Ведь не так-то все и просто...

В конце 90-х довелось написать несколько приложений, которые поддерживали историю операций и позволяли делать операции undo. Undo-операции сохранялись в стеке. При инициировании Undo верхний элемент стека извлекался, проводилась модификация документа, точнее говоря, отменялось соответствующее изменение в документе. В общем-то ничего сложного, если бы не один небольшой нюанс :)

Результаты Undo должны были сказываться не только на самом содержимом документа, но еще и на состоянии редактора.

Например, после последнего изменения документ был сохранен на диск, после чего пользователь выполнил команду Undo. Теперь содержимое документа в памяти и на диске не совпадают. Это значит, что при попытке пользователя выйти из редактора нужно спросить, а не желает ли он сохранить изменения.

Или, напротив, пользователь сохранил документ, затем что-то исправил (документ изменен, нужно спрашивать о сохранении документа при выходе), потом выполнил Undo (документ вновь совпадает с тем, что на диске, ничего при выходе спрашивать не нужно).

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

Или вот еще поворот разговора: допустим, Undo/Redo операции лежат в одном списке и есть текущая позиция в нем. При выполнении Undo позиция смещается влево, при последующем Redo -- вправо.

Ведь очевидно же, что Undo/Redo-список будет не простым списком. Допустим, мы сделали четыре действия, Undo-список пополнился четырьмя элементами:

a1 --> a2 --> a3 --> a4
                     * <-- текущая позиция

Выполняем два Undo и текущая позиция смещается на две позиции влево:

a1 --> a2 --> a3 --> a4
       * <-- текущая позиция

Если мы теперь инциируем Redo, то текущая позиция сместиться на один элемент вправо:

a1 --> a2 --> a3 --> a4
              * <-- текущая позиция

Но если здесь мы не будем делать еще один Redo, а сделаем новое действие над документом, то у нас появится "развилка" -- три элемента старых, к которым будет приписан еще один новый:

a1 --> a2 --> a3 ... a4 <-- между a3 и a4 теперь нет связи, за a3 следует a5
              \
               `---> a5
                     * <-- текущая позиция

Но что делать с тем старым Undo/Redo элементом a4, который уже не нужен, и к которому возвращаться уже нет смысла? Его ведь нужно выкидывать:

a1 --> a2 --> a3 --> a5
                     * <-- текущая позиция

Ну и что это будет если не модификация иммутабельного undo/redo списка? ;)

PS. Давно пытаюсь понять, могут ли существовать полноценные ФЯ без сборки мусора. Такое ощущение, что нет. А это означает, что программам, написанным в функциональном стиле, для нормальной работы нужно будет от 2-х до 4-х раз больше ОП, чем аналогам на императивных языках с ручным управлением памятью. Только вот не знаю, вернутся ли когда-нибудь времена, когда память вновь станет ресурсом...

Комментариев нет: