континуации и stateful веб-программирование (Updated!)

    Идея совсем не нова. Идея древна.
    Однако большинство наблюдаемых вокруг веб-фреймворков упорно игнорируют эту идею.

    Она заключается в том, чтобы использовать континуации (continuations) для магического превращения RESTful (stateless) веб-приложений в более удобный и привычный stateful формат.

    По сути, «сессия» придумана, чтобы хранить состояние выполнения веб-приложения.
    А использование её тупо как набора данных — исключительно из-за отсутсвия в языке полнофункциональных продолжений.

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

    Например, в питоне это реализовано оператором yield.
    Функция, содержащая yield считается «генератором итераторов», её выполнение прекращается на первом операторе yield, а функция возвращает объект типа итератор, к которому можно применять next() для вызова следующей итерации, и send(значения) для возврата значения в точку перехода по yield.
    Вызов итератора методом next() и send() возвращает очередное, следующее, значение.
    Итераторы можно использовать для итерирования чисел (и это может быть безумно весело, если числа, скажем — фиббоначи, и их — бесконечное количество), можно делать обход дерева, можно итерировать всё и вся.
    Точно также итератор может генерить не числа или узлы дерева, а web-страницы.

    Например, функции для просмотра ресурсов и засовывания их в корзину покупок могут выглядеть так:
    browse_stuff():
        # вывести в браузер форму ввода критериев поиска и сохранить введённое в объект 
        criteria = yield(ask_criteria())
        # поиск ресурсов по критериям
        results = find_stuff(criteria)
        # вывести в браузер список ресурсов, листаемый по страницам и чего-нибыдь там ещё
        yield (list_stuff(results))
        # прекратить это гнусное дело
        raise StopIteration()
    
    def buy_stuff(continuation, item)
        if not user.authentificated:
    	# переход на страницу запроса пароля, восстановления логина, регистрации
            user = yield(login_form(user))
        # теперь пользователь залогинен, либо создан новый, и можно продолжать
        # спросить, сколько
        quantity = yield(ask_quantity())
        # спросить, куда доставлять
        delivery = yield(ask_delivery())
        # попросить денег
        money = yield(ask_payment())
        # оформить заказ
        status = launch_order(item,quantity,delivery,money)
        # показать страницу с кнопочкой "вернуться"
        yield(message("спасибо за покупку. ждите курьера."))
        # вернуться туда, где была вызвана функция (например в browse_stuff)
        continuation.next()
        # интересно, оптимизирует ли питон такую "хвостовую рекурсию" ?
    

    Параметр continuation — это состояние приложения (выполнения другой функции) в том месте где был совершён переход по ссылке «купить хрень».

    Основное приложение при таком раскладе выглядит как итератор.
    cont = browse_stuff_iter()
    Объект cont сохраняется в сессии, или где-нибудь ещё, и восстанавливается при каждом обращении клиента.
    Или же сохраняется несколько объектов, и их идентификаторы кодируются в урлах, засунутых в скрытые поля форм, так, что при нажажатии кнопки «back» пользователь не просто видит предыдущую страницу, а реально возвращается в предыдущее состояние приложения.

    На запрос GET вызывается cont.next()
    На запрос POST вызывается cont.send(данные формы)
    Результаты этих вызовов отображаются как страницы.
    Такой метод позволяет генерить цепочку форм как в buy_stuff()
    При переходе по новой ссылке, это расценивается как прерывание и текущее состояние передаётся обработчику: buy_stuff(cont,item)

    Upd:
    Стоит заметить, что ни в питоне, ни темболее в пхп, ни в жаве, нет полноценной поддержки продолжений, на уровне объектов первого класса.
    Питоновский yield ведёт себя очень похоже и был использован для иллюстрации идеи.
    Как оно будет работать в действителности — не совсем понятно.

    Полная поддержка континуаций есть в разных экзотических языках, но и в Ruby в том числе.
    Существует несколько серверов с поддержкой продолжений для scheme, lisp, smalltalk, OCaml и JavaScript.
    Где-то в комментах затерялась ссылка на простой эмулятор продложений для PHP.


    Будущее веба, однозначно — за языком, поддерживающих полноценные континуации!

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

    Ну и, как это обычно бывает, подобные мысли уже высказывались более структурированно и посоледовательно :)

    Список неиспользованной литературы:


    мега-UPD: иллюстрация на scheme
    Не ручаюсь за правильность кода. но скобки сбалансированы :)
    ;; server code
    (define (uri->cont uri)  "выуживает откуда-то продолжение по его URI" )
    (define (cont->uri cont) "сохраняет куда-то продолжение и формирует для него URI )
    
    (define (insert-uri tmpl uri) "вставляет ссылку в шаблон" )
    (define (render-page tmpl args) "рендерит страницу в html" )
    
    (define (handle-request request) "вызывает продолжение, передавая ему request"
      (if (eqv? (get-uri request) init-uri)) ; если запрос на стартовый URI
          (start) ; вызвать старт
          ((uri->cont (get-uri request)) request))) ; иначе - продолжение
    
    (define (make-response cont template) "template: страница со ссылками или форма."
      (render-page (insert-uri template (cont->uri cont))))
    ;; end of server code
    
    ;; application code
    (define (quest room)
      (define (parse-request request) "парсит данные формы или чонить и возвращает choice" )
      (define (walk-on choice) "выбрает новую комнату по текущей и choice" )
      (define (get-page) "возвращает html-шаблон для текущей комнаты" )
      (define (response cc) (make-response cc (get-page)) "тупо карринг параметра" )
      (quest (walk-on (parse-request (call/cc response)))))
    
    ;; initialization code
    (define init-uri "/mytextquest")
    (define (start) (quest 'start-room))
    

    Что тут происходит (должно происходить):
    0. quest вызывается с парамтером идентифицирующим комнату.
    0.5 единственный вызов — последний, самый крайний параметр — выражение (call/cc (response))
    1. call/cc (этотакая специальная конструкция, которая) вызывает функцию (response continuation)
    2. та в свою очередь — вызывает (make-response continuation template)
    3. make-response вставляет в шаблон (например в поле action, или в ссылки перехода) URI идентифицирующий это продолжение.
    4. страница рендерится
    5. юзер кликает по одной из ссылок на странице или субмитит форму
    6. handle-request выуживает по ссылке продолжение
    7. и вызывает его с параметром request
    8. выполнение продолжается с того места, где стоит call/cc. значение request подставляется в качестве результата вызова call/cc
    9. request парсится и мы получаем логику чего там тыкнул юзер
    10. walk-on вычисляет в какую комнату попадёт теперь юзер
    11. quest рекурсивно вызывается с параметром, идентифицирующим следующую комнату

    Если кроме комнаты на выбор пути влияет карма юзера. содержимое карманов. и состояние окружающей экологии — всё это инкапсулируется в 'room'.
    Если юзер нажал 'back' и перешёл на предыдущий URI, выдёргивается предыдущее состояние и он реально попадает в предыдущую комнату, все пострадавшие при переходе животные оживают, итп.
    Поделиться публикацией
    Комментарии 95
    • НЛО прилетело и опубликовало эту надпись здесь
        +1
        > Какие проблемы будет легче решить используя statefull?

        Все, в которых есть понятие «состояния».

        Яркие примеры: авторизация и сессии.
          +4
          сессия — это всё-таки средство, а не проблема :)
          +1
          Более удобный для императивно- и функционально- ориентированных разработчиков, для которых привычнее описывать логику работы приложения путём декомпозиции на отдельные функциональные точки и программированием каждой точки в отдельном блоке (функции, модуле, объекте), а не развермишеливая её по REST-ориентированным запросам.
          Разумеется, для веб разработчика, уже набившего руку на запрос-ответах, это непривычно. Поэтому и стоит тэг «новый взгляд».

          Приемущества:
          более последовательное описание логики,
          отсутствие необходмости заботиться о сохранении состояния,
          отсутствие проблемы кнопки «back» и «clone» (иллюстрировано картинками в «Inverting back the inversion»).

          Если эти приемущества не очевидны, то можно сказать так:
          Это не поможет решить какие-то принципиально новые проблемы.
          Это поможет избежать большинства проблем и задач, возникающих при разработке веб-приложений.
            0
            Уважаемый qmax. Хочется заметить, что для «эмуляции»- достаточно лишь идентификатора сессии.

            Остальное — дело техники. OOP в PHP давно позволяет делать __sleep/__wakeup. Для нормального «восстановления» объекта достаточно написать базовый класс и указать обработчик сериализованного объекта.

            Для того, чтобы писать полноценные (с точки зрения вымышленной здесь проблемы) приложения — достаточно просто чуток подумать головой и посмотреть в ман по нужной области.

            И будущее, кстати, не за языками, а за технологиями. А язык — это вопрос применимости. Сейчас начнут холливарить ведь ;)
              +1
              согласен, что дело в технологиях, а языки — лишь инструменты.
              можно и на асемблере написать ООП.

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

              в качестве продолжения :) этого коммента я написал в апдейте поста иллюстрацию на scheme, использующую нативное использование продолжения в форме call/cc.
              уровень условности реализации, думаю, ясен.

              попробуйте переписать аналогичное на PHP с сериализацией объекта.
              ну просто для сравнения.
                0
                По вашим словам, так за «иллюстрацию на scheme, использующую нативное использование продолжения в форме call/cc.»
                будущее? =))))

                Вот интересный вопрос: «наиболее естественно поддерживает». Вот лично вам не все-равно?
                Для примера: file_get_contents пыховый или нативно-сишный fopen/fread. Нет file_get_contents — напишем для него враппер.

                Вопрос не в том, чтобы «переписать аналогичное». Уверен, что переписать можно что угодно на чем угодно. Просто зачем набиваться на холливары — не совсем ясно =)
                  0
                  Вопрос не в холиварах, а в том, чтобы сравнить разные подходы.
                  У меня получилось в одну строчку. И я считаю это нагляднее и проще.
                  Что получится на php?
          +1
          > По сути, «сессия» придумана, чтобы хранить состояние выполнения веб-приложения.
          > А использование её тупо как набора данных — исключительно из-за отсутсвия в языке полнофункциональных продолжений.

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

          Сериализация в PHP может быть использована для этой так называемой «континуации»?
            0
            в принципе, если инкапсулировать всё приложение вообще в какой-нибудь обект, то его сериализация вполне сойдёт за континуацию.

            вот тут предлагается какаято библиотека для эмуляции продолжений в пхп.

            там какраз класс, от которого можно унаследовать такое приложение.

            +1
            Более удобный для императивно- и функционально- ориентированных разработчиков, для которых привычнее описывать логику работы приложения путём декомпозиции на отдельные функциональные точки и программированием каждой точки в отдельном блоке (функции, модуле, объекте), а не развермишеливая её по REST-ориентированным запросам.
            Разумеется, для веб разработчика, уже набившего руку на запрос-ответах, это непривычно. Поэтому и стоит тэг «новый взгляд».

            Приемущества:
            более последовательное описание логики,
            отсутствие необходмости заботиться о сохранении состояния,
            отсутствие проблемы кнопки «back» и «clone» (иллюстрировано картинками в «Inverting back the inversion»).

            Если эти приемущества не очевидны, то можно сказать так:
            Это не поможет решить какие-то принципиально новые проблемы.
            Это поможет избежать большинства проблем и задач, возникающих при разработке веб-приложений.
              0
              Спасибо за статью, действительно увлекательная тема. Сижу читаю про seaside, ну и smalltalk конечно — интересно как континуации реализованы на практике. Сразу видно несколько косяков, которые вызывают определенные сложности в UX, но с другой стороны многие вещи решаются проще.

              Как относительно новый и интересный подход к разработке WEB приложений — однозначно must для ознакомления.
                +2
                RESTfull => RESTful
                statefull => stateful
                  0
                  упс!
                  ain't a native speaker :)
                  0
                  намного лучше рулить состояниями в определенных workflow фреймворках, типа OsWorkflow

                  продолжения — они несколько для других целей предназначены, и запоминания состояния приложения (Memento) — побочный эффект

                  IMHO
                    0
                    да нет. продолжения какраз для того и придуманы, чтобы сохранять состояние.
                    это основная их функция.
                    а то, что на них можно делать итераторы, сопрограммы, и прочее — следствие.
                      0
                      Спасибо за затронутый вопрос. Буквально намедне пришла в голову мысль: «Почему эту особенность сессии даже толком нигде не описывают?» Единственное. описание в статье несколько сумбурное — затрагивается несколько вопросов, а сводится все к одному.
                      Что касается поддержки языков точек восстановления, это, конечно, удобное подспорье в решении задачи, но не основополагающее. Например, в распределенной бизнес-транзакции одной поддержки языком явно недостаточно.
                        0
                        статья сумбурна, потомучто толком сам ещё не разобрался. это в общемто — просто изложение идеи.

                        а конкретного опыта написания таких приложений пока нет.
                    0
                    тоже как-то интересовался )
                    www.xonix.info/2007/08/continuations.html
                    п.с. таки, генераторы это не совсем продолжения
                      0
                      конкретно в питоне генераторы реализуются ЧЕРЕЗ продолжения. и поэтому их можно заюзать в этом качестве.

                      в оргинале статьи был ещё более явный пример на scheme.
                      но я побоялся ломать мозг ещё и этим :)
                        +1
                        смотря в каком ) в Stateless — да, а в стандартном питоне нет продолжений, и генераторы там созданы на обычных сях, без каких бы то ни было абстракций )
                        да и генераторы могут быть сделаны не только на продолжениях но и, например, на потоках (подробнее тут www.python.org/dev/peps/pep-0255/)
                        а продолжение — гораздо более мощная конструкция

                        почему это не продолжения, например, как сейчас вспомню свою мысль, например можно вернуть продолжение из нескольких вложенных вызовов функций, а с генераторами так не выйдет.
                        Ну и пару ссылок, где еще это обсуждается (отличие генераторов от продолжений)
                        www.hutteman.com/weblog/2005/04/26-224.html
                        zope.stackless.com/spc-sheets-seoul.ppt
                          0
                          оговорился, в Stackless
                            0
                            ну да, естественно, продложения более мощные конструкции.
                            однако yield всёже обладает некоторыми подходящими свойствами.
                            хотя я слабо представляю как можно их сделать на «обычных сях».
                            и интересно посмотреть как оно будет реализовано на PyPy.

                            да и пост в общем-то не про питон :)
                              0
                              да как на обычных сях, очень просто, посмотрите, например файлик genobject.h из стандартного дистрибутива питона
                              подходящими для построения на его основе веб-фреймворка (по принципу продолжений) вроде не обладает, т.е. когда захочется сделать компонентную модель (по типу Seaside), что-то мне подсказывает, что не получится
                        0
                        кстати сказать, именно эта твоя статья меня и побудила написать заметку :)
                        я там даже коммент оставил.
                        0
                        Поясните дураку. При применении данной технологии приложение становится подобием конечного\бесконечного автомата? Таким образом мы выводим проблемы возможности\невозможности какого-либо действия из данной точки на принципиально новый уровень? Я прав?
                          0
                          континуации, в принципе, позволяют реализовать конечный автомат в пределах одной функции. но это одно из применений. (в другой терминологии — «сопрограмма с самой собой»).

                          но автомат подразумевает набор состояний и набор переходов между ними.
                          континуации сами по себе это не предполагают. они предполагают только «возврат».
                          автоматы это всётаки перпендикулярный подход.

                          в частности, эти подходы никак не пересекаются если подумать о недетерминированных автоматах с нечётким состоянием, котрые на континуациях ну никак не вписываются.
                            0
                            А мне моя идея понравилась :) Что если рассматривать веб приложение как последовательность состояний? Тогда можно будет чётко указать возможные действия пользователя. Повысится секьюрность и простота разработки логики. Напишу-ка плагин для рельсов :)
                              0
                              ну в принципе не плохая идея :)

                              но как её применить в этих случаях:
                              1. юзер нажимает кнопку «back» несколько раз и перепостит форму, которая уже была засубмичена
                              2. а потом нажимает кнопку вперёд, пропуская одну из форм.
                              3. юзер делает несколько копий первой формы, и продвигается паралельно по нескольким путям.

                              в случае с континуациями, восстанавливающимися по параметрам запроса, (спрятанным в форму):
                              1. приложение возвращается в предыдущее состояние
                              2. приложение возвращается в следующее состояние, как оно зафиксирвоано в форме
                              3. приложение двигается параллельными путями, как если бы это были разные юзеры

                              в случае с континуациями, восстанавливающимися из сессии (всегда последнее для любого запроса):
                              1. приложение посылает пользователя нафик
                              2. приложение посылает пользователя нафик
                              3. приложение посылает пользователя нафик
                              :))
                                0
                                Посылать — жестоко. По поводу кнопок — изменить состояние приложения. Запрос же идёт в любом случае. По поводу параллельности выполнения одним пользователем — надо подумать плотнее. Могут быть проблемы. Простейший выход — своя сессия для каждой из нитей.
                                0
                                Отличная идея. Удачи вам.
                                Когда я занимался разработкой системного ПО на C++, я использовал фрэймворк который реализовывал подход автоматного программирования. Это было прикольно.
                                  0
                                  Спасибо. Попробую реализовать, когда буду принадлежать себе.
                            0
                            Талию где делать будем с кнопкой Back как поступим?
                              0
                              см. суб коммент выше,
                              и в статье «Inverting back the inversion of control» — с картинками.
                              какраз про кнопку back. и возможные извращения с ней.
                              0
                              Вопрос наверное глупый, но меня он волнует:

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

                              P.S. Статья немного сумбурная, но я получил информацию для размышления.
                                0
                                Сервер может быть не только апач. Например, это может быть сервер приложений (написанный на python, java или smalltalk) который как и апач никогда не завершается и только и занят тем что обрабатывать запросы пользователей. Этот самый сервер приложений может при желании проксироваться через апач. Именно так обычно работают J2EE-сервера, всякие Django и т.д. =)
                                На PHP (CGI, mod_*) свет клином не сошелся )
                                как-то так
                                  0
                                  Еще видимо как-то через fastCGI может быть реализованно.
                                    0
                                    да вот так, по большому счёту, если смотреть с позиции «сессия как состояние» — тут вообще ничо сложного нет — сериализовать вообще весь контекст приложения, и дело с концом.

                                    вопрос в том, как это встраивается в язык программирования.
                                  0
                                  питон не поддерживает континуации как объекты первого класса, поэтому как такового механизма нет.
                                  наверно, добавлю это в пост.

                                  вродебы единственный язык, котрый поддерживает продлжения полностью — это scheme.
                                  но пример на scheme в статье ломал бы мозг два раза а не один :)

                                  где-то выше комментом кидал ссылку с эмулятором продолжений для php.
                                  (фактически — сериализация объекта и сохранение его куда угодно)

                                  а цель статьи и была дать информацию к размышлению :)
                                    0
                                    вы хотите сказать, что папа-CLisp не поддерживает?
                                      0
                                      папа-lisp не поддерживал.
                                      а common-lisp это брат scheme.
                                      причом младший.

                                      см. генеалогию

                                  +1
                                  HTTP в-принципе stateless.

                                  Будущее за новым протоколом, а не языком.

                                  А так это всё извраты, т.е. хаки, хоть и красивые.
                                    0
                                    протокол — да.
                                    но это вовсе не значит, что и приложение должно быть stateless.

                                    если на пальцах:
                                    мы с вами сейчас общаемся тоже по stateless протоколу.
                                    коммент прочитан/коммент написан, и всё.
                                    (сами комменты — это не внутреннее состояние, а его проявления.)
                                    но это не мешает мне отвечать даже в нескольких цепочках.

                                    хотя нет. таки мешает. я не могу ответить одним комментом на несколько сразу и приходится использовать ссылки типа «см. коммент выше» :)
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                        0
                                        если протокол http заменится каким-либо stateful аналогом, то он вполне сможет обеспечить обратную совместимость.

                                        хотя как-то слабо представляется WWW на отличных от REST-подобных принципов.
                                      +1
                                      Континуации звучит как-то не по-русски. Чем вам не угодили продления?

                                      Кстати, идея использования продлений родилась в Smalltalk фреймворке Seaside.
                                      Сейчас она реализована в разных диалектах Лиспа: в PLT Scheme Web Server и Common Lisp фреймворках UnCommon Web и Weblocks.

                                      По поводу REST и состояний: далеко не вск согласны, что RESTful = stateless (см. blog.dhananjaynene.com/2008/11/rest-fomenting-unrest-is-restfulness-a-semantics-game-why-does-rest-require-statelessness/)
                                          0
                                          настаиваю на продлении: продолжение звучит слишком неопределенно, как верно подмечено ниже. :)
                                          статью из Википедии в данном случае можно считать лишь мнением ее автора, поскольку термин новый и консенсуса, как его называть, нет.
                                          см., например: lingvo.yandex.ru/en/?text=continuation&from=os
                                            0
                                            ну в интернете используют в основном вариант «продолжения», сравните:
                                            1 vs 2
                                              +1
                                              потому что люди берут первый перевод, не задумываясь :)
                                              +1
                                              тогда уж «возобновление»

                                              а концепт сам по себе не очень определённый — это и действие, и результат действия, и результат как незадействованный результат действия :)
                                                0
                                                возобновление — это по-английски будет restart, а это уже чуток другая концепция из Common Lisp (функция-реакция на исключительную ситуацию).

                                                «это и действие, и результат действия..» — как раз то, что написано в Лингво про продление
                                                ",,, и результат как незадействованный результат действия" — что вы имели в виду? В принципе, результат есть у любого действия, и его не обязательно задействовать
                                            0
                                            да. тут есть тонкость, которая затронутав комменте выше.
                                            stateless — это протокол.
                                            в принципе, выражение в статье «RESTful (statles)» не совсем предполагает синонимичность.

                                            если не путаю, по принципам REST состояние приложения абстрагируется как «ресурс» (идентифицируемый URI, потомучто там всё ресурс.
                                            в этом свете, «продолжения» — какраз и есть такой ресурс.

                                            статью сейчас покурю. спасибо.

                                            P.S.
                                            слово «континуации» я скалькировал для придания ему большего оттенка терминологичности.
                                            слово «продолжение» в контексте, не предполагающем понимания этого понятия, будет выглядеть немного неопределённо.
                                              0
                                              а Seaside смотреть пока не решаюсь, потомучто совершенно не видел SmallTalkа как такового.
                                                0
                                                Smalltalk — это более consistent Руби ;)
                                                Посмотрите Common Lisp ;))
                                                  0
                                                  common lisp я смотрел в подаче Хювённен Сеппяннен.
                                                  но не шибко досконально.

                                                  и мне он показался несколько «грубым» :)
                                                  по сравнению со scheme.

                                                  сейчас я занимаюсь изучениам scheme, и наверно пока не буду отвлекаться на альтернативы. а может, наоборот, будет полезно…
                                                    0
                                                    это очень плохая подача, гораздо лучше — Practical Common Lisp (есть перевод на русский, если это имеет значение) или On Lisp.
                                                    ну, а в целом, как по мне, разобравшись с CL вы будете хорошо понимать Scheme, а вот наоборот не верно, поскольку тема макросов не будет раскрыта
                                                      0
                                                      вроде бы в scheme макросы продвинутее чем в лиспе?
                                                        0
                                                        Я бы сказал наоброт. Но это, конечно, вопрос философский (по которому, в основном, и разделяются пользователя CL и Scheme. Точнее, те, кто использует CL утверждают, что макросы — это одна из ключевых причин выбора именно его, а Schemer'ы придают им намного меньшее значение).

                                                        попробуйте и то, и то, и сами сможете сказать
                                                0
                                                Не в Seaside родилась идея. Впервые я увидел continuations в Cocoon
                                                0
                                                Огромное спасибо! в закладки и разбираться в контексте…
                                                  0
                                                  В контексте HTTP протокола вообще не вижу никакого позитива в такой реализации. Какую проблему Вам решить не удается изза stateless? Помоему все проблемы уже давно решены и довольно успешно, все к ним привыкли и они имхо совсем не dirty tricks
                                                    +2
                                                    лёхкое противоречие в вашем комменте.

                                                    если «все проблемы уже решены» — это значит они есть.
                                                    неважно, насколько успешными решениями они закрыты.
                                                    и то, что все привыкли — никак не признак отсутствия проблем как таковых.

                                                    континуации же ликвидируют эти проблемы в принципе.
                                                    это не очередной способ решения.
                                                    это подход, при котором проблем не существует и решать нечего.
                                                    0
                                                    Если хранить состояние в сессии, то ссылку на такую страницу дать не получится!
                                                    Для меня это было решающим фактором, чтобы отказаться от этой затеи =)
                                                      0
                                                      состояния можно хранить внутре сервера.
                                                      а иденитфицировать урлом.

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

                                                      я таки попробую сделать квест.
                                                      0
                                                      ой скока можно будет переполнений организовывать… :)
                                                        0
                                                        каким образом?
                                                          0
                                                          ну как же, на сервере надо будет хранить данные необходимые для продолжения континуации :)
                                                            0
                                                            эти данные доступны для заполнения и темболее переполнения настолько же,
                                                            насколько разбалансированность скобок в исполняемом скрипте.
                                                              0
                                                              Попробую выразить свою сумбурную мысль поподробнее.

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

                                                              Соответственно простая форма ввода будет выглядеть на языке поддерживающем континуации примерно так:

                                                              do{
                                                              print_form();
                                                              data=read_form();
                                                              }while (check_data(data));
                                                              print_success();

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

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

                                                                0
                                                                А… так вы об этом.
                                                                Я почемуто подумал об уязвимости через переполнение буферов.

                                                                А так тут ровно теже приколы что и с храненем сессий, разница лишь в масштабах.

                                                                Furthermore, garbage collection can not be used to free this memory, because there are roots outside the system: users’ browsers, bookmarks, brains, and notebooks.

                                                                Поэтому всякие менеджеры континуаций с таймаутами и прочим.

                                                                И ещё есть подозрение, что размер будет зависить от структуры кода, а не только от количества параметров.
                                                                  0
                                                                  конечно, это не подозрение, надо храниить стек и все переменные на стеке, соответственно информацию о всех откртых ресурсах. А это ОЧЕНЬ накладно. Пример:

                                                                  $data = sql.exec(«SELECT * from table»); //здесь селект миллиона строк
                                                                  switch( get_user_action()){

                                                                  case page_out: print_page($data);

                                                                  }

                                                                  сколько потребуется ресурсов сервера чтобы сохранить состояние по выходу в get_user_action()?
                                                                    0
                                                                    засрать всю память — тривиальнейшая задача на абсолютно любом языке, включая русский.
                                                                      0
                                                                      ага, только в веб-языке поддерживающем континуации это необходимое зло :)
                                                                        0
                                                                        в труъ функциональных языках:
                                                                        — нет присваивание переменным;
                                                                        — нет скрытого goto в форме последовательности статментов;
                                                                        — у функций отсутствуют побочные эффекты;

                                                                        например
                                                                        с учётом этих трёх моментов, на уровне трансляции легко вычислить, что в данной континуации вам потребуется только 5 строчек.

                                                                        P.S.
                                                                        а на языках запредельно высокого уровня, транслятор вполне сможет вывести, что вообще весь этот кусок кода — сплошное наебалово, и выполнять его не нужно :))
                                                                          0
                                                                          нет, вы не совсем правы:
                                                                          проблема в том, что при выходе из программы с последующей континуацией, транслятор не знает какие данные потребуются после континуации. В приведенном примере транслятор не знает будет ли дальше использоваться результат sql запроса или нет.
                                                                            0
                                                                            очевидно даже не глядя в код, что после возврата потребуются следующие или предыдущие 5 строчек.
                                                                            такое обращение с данными вписывается в интерфейс итератора и реализуется курсором бд.
                                                                              0
                                                                              а почему вы не можете представить что-то другое на месте этих sql данных?

                                                                              $data=read_from_file($f,100000); //read 100k from file
                                                                              do_some_work_with_data($data);
                                                                              ask_user()
                                                                              do_some_work_with_data($data);

                                                                              вот к примеру, разрыв в точке ask_user(), 100килобайт на ура
                                                                                0
                                                                                в целом — не вижу ничего плохого в том, что технология делает неэффективным написание говнокода.
                                                                                  0
                                                                                  то есть вы предлагаете использовать континуации без плюшек которые они предлагают? :) зачем тогда нужны континуации? :)
                                                                                    0
                                                                                    континуации нужны для сохранения состояния приложения.

                                                                                    а для хранения данных придуманы бызы данных и всякие кэши.
                                                                                      0
                                                                                      разве состояние приложения это не есть:
                                                                                      1) состояние стека
                                                                                      2) состояние перменных
                                                                                      3) открытые ресурсы и их состояние (на каком байте открыт файловый дескриптор, и тп)
                                                                                        0
                                                                                        ну вот как показывает опыт эрланга —
                                                                                        переманных и стэка вообще может не быть.
                                                                                          0
                                                                                          может не быть, но при этом и состояния как такового нет :)

                                                                                          подумайте как вы можете отделить одного пользователя от другого (одно состояние от другого) если у вас они не отличаются :)
                                                        0
                                                        Спасибо за инфу! Всегда считал что общепринятый подход писать полезный код в «контроллерах» запросов это извращение!

                                                        Есть правда одна проблема — в динамических языках далеко не все объекты поддаются сериализации… В python-е приходится писать обработчики set_state/ get_state для pickle. Но как быть с 3rd party классами? Недавно использовал mechanize и нужно было сохранить состояние, а pickle его не берет. Пришлось выкручиваться руками, а как бы было красиво, если просто сохранять состояние…
                                                          0
                                                          эта проблема связана со путаницей между состоянием приложения и состоянием объектов.
                                                          если состояние приложения полностью реализуется ограниченным (в пространстве имён) набором переменных — то сессия целиком и полностью решает все возможные проблемы.

                                                          континуация же не ограничивается сохранением значений переменных (хотя это и не особо видно в псевдо-примерах) — это сохранение состояние вычисления.
                                                          к значениям переменых добавляется ещё и место «прерывания».

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

                                                          надо пробовать.
                                                          оставайтесь на нашей волне :)
                                                          0
                                                          идея понятна, но кто угодно может что угодно городить в теории, пока мы будем хуярить на имеющихся у нас инструментах
                                                            0
                                                            Продукт созданный с использованием продолжений, пока вы, извините, х****е на своих инструментах
                                                              –1
                                                              Ну и на лиспе делают сайты и не плохие.

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

                                                              Я про то, что технологию должен поддерживать инструментарий, чтобы можно было работать на высоком абстрактном уровне.
                                                                0
                                                                Ну я своим комментарием, хотел сказать, что инструменты уже есть.
                                                                  0
                                                                  а сайты на лиспе где видали?
                                                                    –1
                                                                    за 5 минут в гугле нашел пару десятков.
                                                                    ищущий да обрящет…
                                                              +1
                                                              Стоило взять пример из Seaside для более наглядной демонстрации.
                                                              go
                                                              | user |
                                                              user := self call: AuthRegisterComponent new.
                                                              user username ~= ''
                                                              ifTrue: [ self inform: ('Good Job ', user username) ]
                                                              ifFalse: [ self inform: 'You did not register' ]

                                                              Пока выполнятся эти строки, произойдет 4 HTTP запроса.
                                                              А еще посмотрите на это в действии DabbleDB
                                                                0
                                                                в действии смотреть не особо интересно.
                                                                интересней, что у него унутре.
                                                                  0
                                                                  но за ссылку спасибо.

                                                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                Самое читаемое