thedeemon: (Default)
Dmitry Popov ([personal profile] thedeemon) wrote2009-09-22 06:23 pm
Entry tags:

Прогресс в ФЯ

Когда делаю для себя на Окамле что-то интерактивное, то обычно делаю веб-интерфейс. Нашел в интернетах простенький веб-сервер в 200 строк (из них половина - перечисление кодов ошибок HTTP) - thumper, портировал под винду (реализовав нехватавшую функцию из модуля Unix), дописал разбор параметров и POST запросов. Устроено там все было очень просто и по-функциональному: для нужных путей регистрируются обработчики, являющиеся вполне себе чистыми функциями - параметры запроса на входе, заголовки и тело ответа на выходе. Этого вполне хватало, но вот пришел день, когда понадобилось обрабатывать много данных и выводить прогресс выполнения. И тут всплыл закон дырявых абстракций.
Как говорят классики, чистое функциональное программирование - это программирование посредством математических функций. Такие функции зависят только от своих параметров и описанных выше значений (эту часть обычно забывают упомянуть, кстати) и возвращают одно значение, больше ничего не делая, т.к. делать-то они ничего и не могут - функция есть просто отображение, математическая конструкция. В математике просто нет такого понятия, как время вычисления функции, там если она описана, то считай, что для всех возможных входных значений результат уже известен. В этом чистая математика разительно расходится с компьютерной действительностью, где присутствует еще одно измерение - время, отчего и возникает столько проблем с временем выполнения программ на чистых языках. Но это я отвлекся.
В данном случае я поступил следующим образом. Раньше обработчик запроса возвращал тело ответа в виде строки. Теперь же он возвращал значение типа

type content_t = ContString of string | ContEnum of string Enum.t;; 

Т.е. это либо строка, как раньше, (чтобы старые обработчики сильно не менять), либо ленивая последовательность строк. Ее сервер выводит, делая flush после каждой порции. Долго работающая операция в моем случае меняла состояние программы - загружала из кучи файлов данные, которые использовались в последующих запросах. Получилось, что загрузка данных - это сайд-эффект, а возвращаемое значение - постепенное отображение прогресса загрузки. Выглядело это примерно так:

let files_work_enum = files |> List.enum |> Enum.mapi (fun i fname ->  
    res_list := (parse_file !g_dics fname) :: !res_list; (* time consuming operation *) 
    let percent = (i+1) * 100 / nfiles in 
    Printf.sprintf "<script>setPercent(%d);</script>" percent) in ...

Т.е. обработчик запроса возвращает веб-серверу последовательность отложенных вычислений. Веб-сервер последовательно по ней проходится, отдавая результаты пользователю, в результате прогрессбар ползет по мере обработки данных. Все работает, но благодаря тому, что само вычисление стало побочным эффектом. Вот мне интересно, а как такая задача решается в православном чистом ФП?

Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting