Как обрабатывается FRP с точки зрения памяти?

Читая о FRP (Функциональное реактивное программирование), я поражен тем, насколько интуитивным и логичным он кажется по сравнению с стандартным императивным подходом; одна вещь, однако, меня озадачивает. Как компьютеру сразу не хватает памяти, делая это?

Из того, что я узнал из [здесь], в FRP полная история ( прошлое, настоящее и будущее) изменения значения является первоклассным. Это понятие немедленно вызывает тревогу в моей голове, говоря, что оно должно потреблять вашу память очень быстро, если оно используется в среде, где прошлое значения не очищается из памяти немедленно.

Читая о [Фрэн], я заметил, что несколько примеров имеют рекурсивно определенные функции без условие прекращения. Если функция никогда не завершится и не вернет свое значение функции, вызвавшей ее, как она сможет что-то сделать? Или, если уж на то пошло, как это через какое-то время не взрывает стек? Даже такой ленивый язык, как Haskell, в какой-то момент столкнется с переполнением стека.

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


person Electric Coffee    schedule 29.12.2014    source источник


Ответы (2)


Тот факт, что это может работать для простых случаев, не должен вызывать большого удивления: мы уже комфортно используем бесконечные структуры данных в Haskell благодаря лени и сборке мусора. Пока ваш конечный результат не зависит от одновременного наличия всех ваших значений, они могут быть собраны по мере продвижения или не принудительно в первую очередь.

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

fib n = fibs !! n
  where fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)

Попробуйте запустить эту функцию для разных входных данных и посмотрите на использование памяти. (Запустите его с помощью +RTS -s.)

(Если вам нужно более подробное объяснение с диаграммами, взгляните на этот пост, который я написал.)

Дело в том, что даже если программисту доступен неограниченный объем информации, мы все равно можем собрать большую ее часть, если от нее больше ничего не зависит.

Точно такая же логика может быть использована для эффективной реализации программ FRP.

Конечно, все не так просто. В примере fibs использование памяти увеличилось бы, если бы у нас был активный указатель на начало списка fibs. То же самое происходит с FRP, если ваши вычисления зависят от слишком большого количества прошлых данных: это называется утечкой времени.

Работа с утечками времени — одна из открытых проблем при внедрении эффективной и надежной структуры FRP. Трудно предоставить выразительные абстракции FRP, не допуская возможности плохого или даже катастрофического использования памяти. Я считаю, что большинство современных подходов в конечном итоге предоставляют абстрактные типы FRP вместе с благословенным набором операций, которые с меньшей вероятностью вызовут такого рода утечки; особенно экстремальной формой этого является Arrowized FRP, который вообще не предоставляет тип поведения/сигнала, а скорее выражает все с помощью преобразований между сигналами (в виде стрелок).

Я никогда не пытался реализовать хорошую систему FRP самостоятельно, поэтому я не могу более подробно объяснить проблемы. Если вас интересуют более подробные сведения по этой теме, отличным местом для поиска является блог Конала Эллиотта с этот пост в качестве хорошей отправной точки. Вы также можете ознакомиться с некоторыми из написанных им статей, например "Push-Pull Functional Reactive Programming", а также другие статьи по этой теме, в том числе некоторые о Arrowized FRP, такие как "Функциональное реактивное программирование, продолжение" (выбрано почти случайно).

сноски

¹ Это не на самом деле постоянное пространство, потому что промежуточные результаты сами увеличиваются. Но он должен поддерживать постоянное количество ячеек списка в памяти.

person Tikhon Jelvis    schedule 30.12.2014
comment
Но если всю историю я был первоклассным гражданином, должен быть способ как-то вернуться к началу, верно? Как и в Elm, языке, построенном на основе FRP, в нем даже есть отладчик, путешествующий во времени, который позволяет вам прокручивать временную шкалу программы по желанию. Это должно где-то создавать утечку времени, не так ли? - person Electric Coffee; 30.12.2014
comment
Дело в том, что это может вызвать утечку только в том случае, если вы действительно нужны данные с самого начала — и такие утечки случаются. Но если ваша программа написана таким образом, что не использует все эти данные, она может быть безопасно удалена сборщиком мусора. - person Tikhon Jelvis; 30.12.2014
comment
Я уверен, что отладчику Elm приходится много хранить в памяти просто потому, что это невозможно обойти. Но обычная программа Elm может собрать большую часть этого, обеспечивая разумное использование памяти. - person Tikhon Jelvis; 30.12.2014
comment
Это по-прежнему не объясняет, как рекурсивные функции без завершающего условия каким-то образом умудряются возвращать значение; как он узнает, когда прекратить рекурсивно вызывать себя? - person Electric Coffee; 30.12.2014
comment
@ElectricCoffee Как правило, вы должны использовать корекурсию, а не рекурсию. Пока он создает конструктор после конечного числа шагов, он завершается. - person Carl; 30.12.2014
comment
@ElectricCoffee: эти функции работают, потому что их возвращаемое значение всегда является ленивым конструктором: структурой данных, которая объединяет код, говорящий, как вычислять значения полей. С точки зрения более низкого уровня, объектный код, сгенерированный для таких рекурсивных функций, не вызывает сам себя; скорее, он возвращает структуру с указателем на саму функцию. Потребитель возвращаемой структуры, получая доступ к полям или воздерживаясь от этого, выбирает, произойдет ли и когда рекурсивный вызов на самом деле. - person Luis Casillas; 30.12.2014
comment
Я хотел бы отметить, что проблема утечки времени была решена в последние годы. старый пост в блоге описывает проблема и возможные решения. последнее сообщение описывает решение в последних версиях реактивного банана. - person Heinrich Apfelmus; 31.12.2014
comment
FFR, подход, упомянутый @HeinrichApfelmus, также описан в Практических принципах FRP: забудьте прошлое, измените будущее, FRPNow! бумага 2015 года - person cubuspl42; 17.01.2021

Что касается части вашего вопроса об утечке времени: это действительно одна из основных проблем при внедрении FRP. Однако исследователи и разработчики FRP нашли несколько способов их избежать.

Все зависит от конкретного API, который вы предлагаете для сигналов. Главный вопрос заключается в том, предоставляете ли вы FRP более высокого порядка. Это часто принимает форму примитива «монадного соединения» для сигналов: способ преобразования сигнала сигналов в сигнал или, другими словами, API для создания сигнала, который динамически переключается между рядом других сигналов. Такой API очень мощный, но может привести к утечке времени, то есть к проблеме, о которой вы спрашиваете: необходимости хранить все предыдущие значения сигнала в памяти. Однако, как упоминает Генрих Апфельмус в комментарии к предыдущему ответу, есть способы решить эту проблему, ограничивая API-интерфейсы более высокого порядка определенным образом, используя систему типов или иным образом. См. этот комментарий для ссылок на дальнейшие объяснения.

Многие библиотеки FRP просто не предлагают API более высокого порядка и, таким образом, (довольно легко) избегают проблемы утечек времени. Вы упомянули Elm, в данном случае, как указано здесь в разделе "Сигналы не являются монадами". в Эльме». Это происходит за счет выразительности, потому что не предлагается мощный монадический API, но не все считают, что вам нужна общая мощь такого API в фреймворке/библиотеке FRP.

Наконец, я рекомендую интересную презентацию главного автора Elm Эвана Чаплицки, который отлично справляется со своей работой. объяснения этих проблем и предоставления обзора возможных путей их решения. Он классифицирует подходы FRP в зависимости от того, как они их решают.

person Dominique Devriese    schedule 02.01.2015