Как функциональные языки моделируют побочные эффекты?
Поскольку побочные эффекты нарушают прозрачность ссылок, не противоречат ли они функциональным языкам?
4 ответа
Есть два метода, которые используются чисто функциональными языками программирования для моделирования побочных эффектов:
1) Тип мира, представляющий внешнее состояние, где каждое значение этого типа гарантируется системой типов для использования только один раз.
На языке, который использует этот подход, функция print
а также read
может иметь типы (string, world) -> world
а также world -> (string, world)
соответственно.
Они могут быть использованы так:
let main w =
let w1 = print ("What's your name?", w) in
let (str, w2) = read w1 in
let w3 = print ("Your name is " ^ name, w2) in
w3
Но не так:
let main w =
let w1 = print ("What's your name?", w) in
let (str, w2) = read w in
let w3 = print ("Your name is " ^ name, w2) in
w3
(потому что w используется дважды)
Все встроенные функции с побочными эффектами будут принимать и возвращать мировое значение. Поскольку все функции с побочными эффектами являются либо встроенными, либо вызывают другие функции с побочными эффектами, это означает, что все функции с побочными эффектами должны принимать и возвращать мир.
Таким образом, невозможно вызвать функцию с побочными эффектами дважды с одинаковыми аргументами, и ссылочная прозрачность не нарушается.
2) Монада ввода / вывода, в которой все операции с побочными эффектами должны выполняться внутри этой монады.
При таком подходе все операции с побочными эффектами будут иметь тип io something
, Например print
будет функция с типом string -> io unit
а также read
будет иметь тип io string
,
Единственный способ получить доступ к значению выполняемой операции - это использовать операцию "monadic bind" (называемую >>= в haskell, например) с операцией ввода-вывода в качестве одного аргумента и функцией, описывающей, что делать с результатом в качестве другого операнд.
Пример сверху будет выглядеть так с монадическим IO:
let main =
(print "What's your name?") >>=
(lambda () -> read >>=
(lambda name -> print ("Your name is " ^ name)))
Существует несколько вариантов обработки ввода-вывода на функциональном языке.
- Не будь чистым. Многие функциональные языки не являются чисто функциональными. Более того, они поддерживают функциональное программирование, а не обеспечивают его соблюдение. Это, безусловно, наиболее распространенное решение проблемы ввода / вывода в функциональном программировании. (Примеры: Лисп, Схема, Стандарт МЛ, Эрланг и т. Д.)
- Потоковое преобразование. Ранний ввод / вывод Haskell был сделан таким образом. Проверьте мою ссылку ниже для деталей, если вы хотите больше информации. (Подсказка: вы, вероятно, нет.)
- Ввод-вывод с продолжением ("прохождение мира", упомянутое в других ответах). В этом примере вы передаете токен данных вместе с вашим вводом / выводом, который действует как необходимое "другое значение" для поддержания ссылочной целостности. Это используется несколькими диалектами ML, если память служит.
- Описанное выше слово "продолжение" или "мир" может быть заключено в различные типы данных, наиболее известной из которых является использование монад в этой роли в Haskell. Обратите внимание, что это, по идее, то же самое под покровом, но утомление отслеживания переменных состояния "мир"/"продолжение" удалено.
Есть научная диссертация, которая исчерпывающе анализирует их.
Функциональный ввод-вывод является постоянной областью исследований, и существуют другие языки, которые решают эту проблему интересными и сложными способами. Логика Хоара используется в некоторых исследовательских языках. Другие (например, Меркурий) используют уникальную типизацию. Третьи (например, Clean) используют системы эффектов. Из них у меня очень, очень ограниченное воздействие только на Меркурий, поэтому я не могу комментировать детали. Однако есть статья, в которой подробно описывается система ввода-вывода Clean, если вы заинтересованы в этом направлении.
Насколько я понимаю, если вы хотите иметь побочные эффекты на функциональном языке, вы должны явно их кодировать.
Поскольку побочные эффекты нарушают ссылочную прозрачность, не противоречат ли они сути функциональных языков?
Это зависит от функционального языка:
Стандартный ML позволяет свободно использовать побочные эффекты, как и большинство процедурных языков, например Fortran, Algol, Pascal, C и т. д.
Haskell ограничивает побочные эффекты за счет использования абстрактных типов данных, таких как
, а также , что помогает сохранить ссылочную прозрачность. Clean также ограничивает побочные эффекты, но делает это с помощью расширенной системы типов.
Функциональный язык, который использует Coq — Gallina — вообще не предоставляет доступа к побочным эффектам .
Как функциональные языки моделируют побочные эффекты?
Один подход, который не упоминается регулярно, основан на псевдоданных: отдельные одноразовые абстрактные значения, передаваемые в доступном структурированном значении (обычно в виде дерева), с побочными эффектами, возникающими только при первоначальном использовании каждого абстрактного значения. Дополнительные сведения см. в книге Ф. Уоррена Бертона « Недетерминизм с ссылочной прозрачностью в языке функционального программирования» . Рабочий пример также можно найти в GHC: егоUnique
тип поддержки имени.