В качестве небольшого экскурса для непосвященных в логическое программирование
в этом тексте будет проведен сеанс магии с разоблачением приведен подход
к созданию REST-сервера и замерены его параметры.

В чем подвох? А все просто — будем делать на Прологе (в реализации SWI-prolog)…

Итак, что нужно. Во-первых скачать (если еще нет) и поставить собственно swi-prolog
с его сайта www.swi-prolog.org. Проще создать небольшой файл типа rest.pl с начальным содержимым типа
сервер.
и дальше загрузить его и редактировать уже средствами самой пролог-системы, вот так
(примеры приведены для консоли linux, но не сильно отличаются от windows):

  $echo "сервер." > rest.pl
  $swipl
   ...
  ?- [rest].
  true.

  ?- edit(сервер).

После этого запустится emacs-подобный редактор и можно все набирать в нем.

Итак, в начале все же укажем что мы будем пользоваться библиотеками и напишем что значит
запуск сервера — в прологе символы :- можно читать как «это есть»:

:- use_module(library(http/http_server)).
:- use_module(library(http/http_json)).
:- http_handler(/,сервис(M), [method(M),methods([get,post]),time_limit(10000)]).

сервер :- http_server(http_dispatch, [port(8080)]).

Как видно, мы планируем что сервис будет отвечать на методы get и post.

Модельная задача


Теперь собственно, что мы будем делать. По запросу get будем отвечать страницей с полем ввода числа, при его вводе будем запрашивать метод post и вычислять это число Фиббоначии.

То есть:

сервис(get,_) :- форма(X),ответить(X).
сервис(post,Запрос) :-
  http_parameters(Запрос,[val(Число,[integer])]),
  обсчитать(Число).

Ну вот и все! Мы уже на самом деле написали наш сервер… Пролог же декларативный язык — вот мы и декларировали варианты обработки. Кстати, язык можно использовать любой — я решил местами писать на русском.

Но погодите, что же он делает? Ведь 'форма', 'ответить' и 'обсчитать' — это наши предикаты и они не определены пока. Давайте это исправим:

ответить(Вставка):-
 format('Content-type: text/html~n~n
 <html><body>Вычисление чисел Фиббоначчи<br/>
 ~w
 </body></html>~n', [Вставка]).

форма('<form method="POST"><input name="val"/></form>').

Ну и приведем два варианта для обсчитать — пусть у нас числа Фиббоначчи только положительные будут:

обсчитать(Число):-
  Число > 0,
  фиббоначи(0,1,1,Число,Ответ),форма(X),
  format(atom(Строка),'~w число Фиббоначи равно ~w<br/>~w',[Число,Ответ,X]),
  ответить(Строка).
обсчитать(Число):- 
  форма(X),
  format(atom(Строка),'заданное число ~w меньше 0<br/>~w',[Число,X]),
  ответить(Строка).

Ну и осталось определить собственно что такое число Фиббоначи:

фиббоначи(_F, F1, N, N, F1) :- !.
фиббоначи(F0, F1, I, N, F) :-
        F2 is F0+F1,
        I2 is I + 1,
        !,фиббоначи(F1, F2, I2, N, F).

Это определение, конечно, не по фен-шую не совсем привычное, зато считается чуть ли не быстрее чем если бы мы писали на С…

Итак, будет ли это работать? Проверяем:

 ?- сервер.
 % Started server at http://localhost:8080/
 true.

Так, сервер вроде стартовал. Кстати, он многопоточный! Для проверки нужно открыть адрес
127.0.0.1:8080/ и ввести какое-нибудь число — например, 1000:
Вычисление чисел Фиббоначчи
1000 число Фиббоначи равно
4346655768693745643568852767504062580
2564660517371780402481729089536555417
9490518904038798400792551692959225930
8032263477520968962323987332247116164
2996440906533187938298969649928516003
704476137795166849228875
Ну что же, оно работает!

Небольшое исследование производительности сервера — проверим метод GET (POST очевидно сильно зависит от заданного числа, так 10000000 число считается, конечно, но несколько секунд...)

 $ ab -k -c 4 -n 4000 http://127.0.0.1:8080/

...
Concurrency Level:      4
Time taken for tests:   0.283 seconds
Complete requests:      4000
Failed requests:        0
Keep-Alive requests:    4000
Total transferred:      1108000 bytes
HTML transferred:       544000 bytes
Requests per second:    14140.57 [#/sec] (mean)
Time per request:       0.283 [ms] (mean)
Time per request:       0.071 [ms] (mean, across all concurrent requests)
Transfer rate:          3825.14 [Kbytes/sec] received
...

Что же 14140 запросов в секунду при 10 потоках — это очень неплохо для обычного компа!

И да, пролог имеет такую возможность — логический вид. Так что если вы поменяете что-то в коде, то вам достаточно в консоли набрать

 ?- make.

И все ваши изменения будут работать в новых запросах — ничего не нужно перегружать, останавливать и т.д.

Надеюсь, вам было интересно посмотреть, как создают rest-сервера на таком простом примере. Конечно, можно описывать rest интерфейс статически, как и сделано в примере, можно вводить всякие переменные, использовать часть пути URL как переменные — ну в общем все как обычно.

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

Комментарии (9)


  1. Sly_tom_cat
    15.10.2019 18:43
    +1

    Неожиданное применение языка (по крайней мере для меня).

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

    Вы сломали этот шаблон, за что вам — спасибо!


    1. Homas
      16.10.2019 08:05

      Чтобы ещё больше сломать посмотри на cowboy — http сервер написанный на Erlang.
      Вот мой DNS сервер с REST API (использую cowboy) на Erlang. Да и вообще историю Erlang почитайте для чего он разрабатывался.


      1. begemot_sun
        16.10.2019 09:40

        Судя по readme ваш DNS-сервер выглядит законченным продуктом.
        Вы на нём зарабатываете как-то?


        1. Homas
          16.10.2019 11:00

          В данный момент — нет. Как и любой open source продукт монетизация осуществляется только за счет консалтинга/поддержки, «пожертвований» и построение сервисов вокруг него (в этом плане я двигаю ioc2rpz community).
          + Продукт ориентирован на профи, средний и большой enterprise. Потенциал есть, но нужно значительно больше времени (чего нет) тратить на продвижение ну и потом на предоставление «услуг». Пока из публичных мероприятий я демонстрировал его на 2х DefCon и BlackHat.
          Из неявного (что возможно оказывает больший profit) — «делаю себе имя» на данном продукте. :)


  1. oam2oam Автор
    15.10.2019 18:46

    Спасибо! На самом деле, конечно, именно пролог в каком-то смысле один из лучших кандидатов — в нем в единственном реальная иммутабельность переменных (ну там тоже есть способ это обойти, но не простой), да и других удивительных возможностей немало…


    1. khabib
      15.10.2019 20:52

      В Erlang'е реальная иммутабельность переменных?


      1. oam2oam Автор
        15.10.2019 21:02

        скорее все же единичное присваивание, но да — ведь эрланг происходит в том числе и от пролога.


  1. GroGo
    16.10.2019 13:21

    Хотелось бы добавить что есть специальный предикат для работы с html в swi-prolog
    Писал лет 7 назад небольшой проект на прологе под веб — это было великолепно если честно. Очень хорошие впечатления оставил язык.


  1. trapwalker
    16.10.2019 14:35

    Никогда не понимал почему разработчики всяческих RPG (игр) не используют пролог для декларативного описания квестов и обработки их логики в ходе игры.
    А тут вон оно еще что бывает...