Во-первых, существует много общего между функциональным и логическим программированием. То есть множество понятий, разработанных в одном сообществе, можно использовать и в другом. Обе парадигмы начинались с довольно грубых реализаций и стремятся к чистоте.
Но вы хотите знать различия.
Так что я возьму Хаскель с одной стороны и Пролог с ограничениями с другой. Практически все современные системы Prolog предлагают ограничения, такие как B, Ciao, ECLiPSe, GNU, IF, SICStus, SWI, YAP, XSB. В качестве аргумента я буду использовать очень простое ограничение dif/2
, означающее неравенство, которое присутствовало даже в самой первой реализации Пролога - поэтому я не буду использовать ничего более продвинутого, чем это.
Чего не хватает функциональному программированию
Самое фундаментальное различие вращается вокруг понятия переменной. В функциональном программировании переменная обозначает конкретное значение. Это значение не должно быть полностью определено, но только те части, которые определены, могут использоваться в вычислениях. Рассмотрим в Хаскеле:
> let v = iterate (tail) [1..3]
> v
[[1,2,3],[2,3],[3],[],*** Exception: Prelude.tail: empty list
После 4-го элемента значение не определено. Тем не менее, вы можете безопасно использовать первые 4 элемента:
> take 4 v
[[1,2,3],[2,3],[3],[]]
Обратите внимание, что синтаксис в функциональных программах хитро ограничен, чтобы избежать оставления переменной неопределенной.
В логическом программировании переменная не должна ссылаться на конкретное значение. Итак, если нам нужен список из 3 элементов, мы можем сказать:
?- length(Xs,3).
Xs = [_G323, _G326, _G329].
В этом ответе элементы списка являются переменными. Все возможные экземпляры этих переменных являются допустимыми решениями. Как Xs = [1,2,3]
. Теперь предположим, что первый элемент должен отличаться от остальных элементов:
?- length(Xs,3), Xs = [X|Ys], maplist(dif(X), Ys).
Xs = [X, _G639, _G642],
Ys = [_G639, _G642],
dif(X, _G642),
dif(X, _G639).
Позже мы можем потребовать, чтобы все элементы в Xs
были равны. Прежде чем я напишу это, я попробую это один:
?- maplist(=(_),Xs).
Xs = [] ;
Xs = [_G231] ;
Xs = [_G231, _G231] ;
Xs = [_G231, _G231, _G231] ;
Xs = [_G231, _G231, _G231, _G231] .
Видите, что ответы содержат всегда одну и ту же переменную? Теперь я могу объединить оба запроса:
?- length(Xs,3), Xs = [X|Ys], maplist(dif(X), Ys), maplist(=(_),Xs).
false.
Итак, что мы показали здесь, так это то, что не существует трехэлементного списка, в котором первый элемент отличается от других элементов и все элементы равны.
Эта общность позволила разработать несколько языков ограничений, которые предлагаются в качестве библиотек для систем Prolog, наиболее выдающимися являются CLPFD и CHR .
Нет простого способа получить подобную функциональность в функциональном программировании. Вы можете подражать вещам, но эмуляция не совсем то же самое.
Чего не хватает в логическом программировании
Но есть много вещей, которых не хватает в логическом программировании, которые делают функциональное программирование настолько интересным. В частности:
Программирование высшего порядка. Функциональное программирование имеет здесь очень давнюю традицию и разработало богатый набор идиом. Для Пролога первые предложения относятся к началу 1980-х годов, но они все еще не очень распространены. По крайней мере, ISO Prolog теперь имеет гомолог для применения под названием call/2, call/3 ...
.
Лямбда: Опять же, можно расширить логическое программирование в этом направлении, наиболее известной системой является Лямбда-пролог . Совсем недавно лямбды были разработаны также для ISO Prolog .
Системы типов: были попытки, подобно Меркурию, но он не завоевал особого внимания. И нет системы с функциональностью, сопоставимой с классами типов.
Чистота: Haskell полностью чист, функция Integer -> Integer - это функция. Нет мелкого шрифта, скрывающегося вокруг. И все же вы можете выполнять побочные эффекты. Сопоставимые подходы развиваются очень медленно.
Есть много областей, в которых функциональное и логическое программирование более или менее пересекаются. Например, откат назад, ленивость и списки, ленивая оценка и freeze/2
, when/2
, block
. DCG и монады. Я оставлю обсуждение этих вопросов другим ...