Каково практическое использование лени как встроенной языковой функции?

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

Учитывая, что вы хотите выполнять функциональное программирование, какие случаи использования, когда встроенная лень позволяет вам выражать вещи более четко, просто или кратко?

Просто заявлено: Почему лень настолько важна, что вы хотите его встроить в язык?

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

Ответ 1

"Ничто не оценивается до тех пор, пока оно не понадобится в другом месте" - это упрощенная метафора, которая не охватывает все аспекты ленивой оценки (например, в ней не упоминаются явления строгости).

С теоретической точки зрения, при разработке чистого языка существует 3 способа (конечно, если они основаны на каком-то лямбда-исчислении, а не на более экзотических оценочных моделях): строгий, нестрогий и тотальный.

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

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

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

С практической точки зрения, нестрогая семантика позволяет вам более легко определять контрольные абстракции, поскольку структуры управления по существу не являются строгими. На строгом языке вам все равно нужны места с нестрогой семантикой. Например. Конструкция if имеет нестрогую семантическую четность даже в строгих языках.

Так что, если ваш язык строг, контрольные структуры - это особый случай. Напротив, нестрогий язык может быть равномерно нестрогим - он не имеет неотъемлемой необходимости в строгих конструкциях.

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

Ниже приведены несколько общих примеров для лени, проиллюстрированных примерами, отличными от игрушек:

  • Случаи, когда поток управления трудно предсказать. Подумайте об атрибутных грамматиках, когда без лень вам нужно выполнить топологический сортировку по атрибутам для разрешения зависимостей. Повторная сортировка кода при каждом изменении графика зависимостей нецелесообразна. В Haskell вы можете реализовать формализм грамматики атрибута без явной сортировки, и в Hackage есть как минимум две фактические реализации. Грамотные атрибуты имеют широкое применение в построении компилятора.

  • Подход "генерировать и искать" для решения многих проблем оптимизации. На строгом языке вам необходимо чередовать генерацию и поиск, в Haskell вы просто составляете отдельные функции генерации и поиска, а ваш код остается синтаксически модульным, но чередуется во время выполнения. Подумайте о проблеме коммивояжера (TSP), когда вы создаете все возможные туры и затем просматриваете их с помощью алгоритма с ветвями и границами. Обратите внимание, что ветвь связанных алгоритмов проверяет только определенные первые города тура, генерируются только необходимые части маршрутов. TSP имеет несколько приложений даже в самых чистых формулировках, таких как планирование, логистика и производство микрочипов. Немного измененный, он появляется как суб-проблема во многих областях, таких как секвенирование ДНК.

  • Ленивый код имеет немодульный поток управления, поэтому одна функция может иметь много возможных потоков управления в зависимости от среды, в которой она выполняется. Это явление можно рассматривать как своего рода "полиморфизм потока управления", поэтому ленивый абстракции потока управления более общие, чем их строгие аналоги, а стандартная библиотека функций более высокого порядка гораздо более полезна на ленивом языке. Подумайте о генераторах Python, циклах и итераторах списков: в функциях списка Haskell охватываются все три режима работы, при этом поток управления адаптируется к различным сценариям использования из-за лени. Это не ограничивается списками - подумайте о Data.Arrow и повторите, ленивые и строгие версии государственной монады и т.д. Также обратите внимание, что немодульный поток управления является одновременно преимуществом и недостатком, поскольку он делает более сложным рассуждение о производительности.

  • Ленивые, возможно, бесконечные структуры данных полезны вне игрушечных примеров. См. Работы Коналла Эллиота по воспоминаниям более высоких функций порядка с помощью попыток. Бесконечные структуры данных кажутся бесконечными поисковыми пространствами (см. 2), бесконечными циклами и неубывающими генераторами в смысле Питона (см. 3).

Ответ 2

Mac OS X Core Image - хороший практический пример ленивой оценки.

В основном, Core Image позволяет создавать ориентированный ациклический граф генераторов изображений и фильтров. Никакая оценка не выполняется до последнего шага в процессе: материализация. Когда вы запрашиваете материализацию графа Core Image, конечный кадр изображения распространяется назад через преобразования графа, тем самым минимизируя количество фактических значений пикселей, которые необходимо оценить.