Наиболее распространенная модель использования базы данных на функциональном языке, если нет побочных эффектов?
Я пытаюсь осмыслить основную концепцию функциональных языков:
"Центральным понятием в функциональных языках является то, что результат функции определяется ее вводом и только ее вводом. Никаких побочных эффектов!"
http://www.haskell.org/haskellwiki/Why_Haskell_matters
Мой вопрос: если функция вносит изменения только в своей локальной среде и возвращает результат, как она может взаимодействовать с базой данных или файловой системой? По определению, это не будет доступ к тому, что в действительности является глобальной переменной или глобальным состоянием?
Какой шаблон наиболее часто используется для обхода или решения этой проблемы?
3 ответа
Наиболее распространенная модель для устранения побочных эффектов и примесей в функциональных языках:
- будь прагматичным, а не пуристом
- обеспечить встроенные модули, которые позволяют нечистый код и побочные эффекты
- используйте их как можно меньше!
Примеры:
- Лиспа / Scheme:
set!
- Clojure: ссылки и использование методов мутации на объектах Java
- Scala: создание переменных с
var
- ML: не уверен в специфике, но Википедия говорит, что это допускает некоторую нечистоту
Haskell немного обманывает - его решение состоит в том, что для функций, которые обращаются к файловой системе или базе данных, состояние всей вселенной в этот момент, включая состояние файловой системы / db, будет передано функции.(1) Таким образом, если вы можете повторить состояние всей вселенной в этот момент, то вы можете получить одинаковые результаты дважды с помощью такой функции. Конечно, вы не можете повторить состояние всей вселенной в этот момент, поэтому функции возвращают разные значения...
Но решение Хаскелла, ИМХО, не самое распространенное.
(1) Не уверен в специфике здесь. Спасибо CAMcCann за указание на то, что эта метафора используется слишком часто и, возможно, не настолько точна.
То, что функциональный язык является функциональным (возможно, даже полностью чистым, как Haskell!), Не означает, что программы, написанные на этом языке, должны быть чистыми при запуске.
Подход Haskell, например, при работе с побочными эффектами, может быть объяснен довольно просто: пусть вся программа сама будет чистой (это означает, что функции всегда возвращают одинаковые значения для одних и тех же аргументов и не имеют побочных эффектов), но пусть возвращаемое значение main
Функция - это действие, которое можно запустить.
Попытка объяснить это с помощью псевдокода, вот некоторая программа на императивном, нефункциональном языке:
main:
read contents of abc.txt into mystring
write contents of mystring to def.txt
main
Вышеприведенная процедура - это просто последовательность шагов, описывающая, как выполнить серию действий.
Сравните это с чисто функциональным языком, таким как Haskell. В функциональных языках все является выражением, включая основную функцию. Таким образом, можно прочитать эквивалент вышеупомянутой программы следующим образом:
main = the reading of abc.txt into mystring followed by
the writing of mystring to def.txt
Так, main
является выражением, которое при оценке вернет действие, описывающее, что делать для выполнения программы. Фактическое выполнение этого действия происходит за пределами мира программистов. И это действительно, как это работает; ниже приведена актуальная программа на Haskell, которую можно скомпилировать и запустить:
main = readFile "abc.txt" >>= \ mystring ->
writeFile "def.txt" mystring
a >>= b
можно сказать, что означает "действие a
с последующим результатом a
приведены в действие b
"В этой ситуации и результатом оператора являются комбинированные действия a и b. Вышеприведенная программа, конечно, не является идиоматическим Haskell; ее можно переписать следующим образом (удалив лишнюю переменную):
main = readFile "abc.txt" >>=
writeFile "def.txt"
... или, используя синтаксический сахар и нотацию:
main = do
mystring <- readFile "abc.txt"
writeFile "def.txt" mystring
Все вышеперечисленные программы не только эквивалентны, но и идентичны для компилятора.
Вот как файлы, системы баз данных и веб-серверы могут быть написаны как чисто функциональные программы: путем потоковой передачи значений действий через программу, чтобы они были объединены, и, наконец, в конечном итоге в main
функция. Это дает программисту огромный контроль над программой, и поэтому чисто функциональные языки программирования так привлекательны в некоторых ситуациях.
Доступ к базе данных ничем не отличается от других случаев ввода-вывода, таких как print(17)
,
В нетерпеливо оцененных языках, таких как LISP и ML, обычный подход к эффективному программированию просто использует побочные эффекты, как в большинстве других языков программирования.
В Haskell решением проблемы ввода-вывода является использование монад. Например, если вы проверите HDBC, библиотеку базы данных haskell, вы увидите множество функций, возвращающих действия ввода-вывода.
Некоторые языки, такие как Clean, используют типы уникальности для обеспечения такой же последовательности, которую Haskell делает с монадами, но в настоящее время эти языки труднее найти.