Рубрика «functional programming» - 6

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

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

$jNumber = _do(function() {
    $number  = yield literal('-')->orElse( literal('+') )->orElse( just('') );
    $number .= yield takeOf('[0-9]')->onlyIf( notEmpty() );
    if ( yield literal('.')->orElse( just(false) ) ) {
        $number .= '.'. yield takeOf('[0-9]');
    }
    return +$number;
});

Кроме собственно функционального подхода можно обратить внимание на использование классов для создания DSL-подобного синтаксиса и на использование генераторов для упрощения синтаксиса комбинаторов.

Читать полностью »

Elixir: Готовим парсинг правильно — yecc и leex - 1

Лексический анализ (токенизация) и парсинг — одни из наиболее важных концепцпий в информатике и программировании. Эти концепции базируются на огромном количестве теоретических знаний, но сегодня мы о них не будем говорить, потому что их действительно много. Кроме того, подход к парсингу через "науку" может вызвать жёсткое отвращение и напугать. Между тем, практическое применение очень простое и прямолинейное. Если хотите знать больше о теории — идите в Википедию (лексический анализ и парсинг), или читайте восхитительную книгу дракона (рекомендовано к прочтению вообще всем программистам).

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

Читать полностью »

Elixir: Развёртывание приложений с помощью Edeliver - 1

Мы уже обсуждали сборку и развёртывание приложений Elixir(перев: с помощью exrm): как осуществлять миграции поверх релиза или как работать с переменными среды. Пришло время открыть для себя ещё один инструмент, который поможет развёртывать Elixir приложения.

Практика развёртывания Elixir приложений и дальнейшее отслеживание их работы на нодах с помощью Exrm позволяет нам чувствовать себя гораздо увереннее в вопросах управления релизами в production. Однако возникает следующий вопрос: как управлять самим процессом развёртывания? Конечно, мы можем воспользоваться Capistrano, особенно если в мир Elixir мы пришли из Rails. Но посмотрим на цитату из Edeliver README:

edeliver основан на доставке и предоставляет bash-скрипт для сборки и развёртывания Elixir и Erlang приложений, а так же позволяет совершать "горячее" обновление кода.

Пытаться организовать весь процесс развёртывания вручную — это жёсткая головная боль с кучей повторяющегося кода. А вот использование Edeliver для развёртывания оказалось очень простым с первой же попытки! В конце концов, весь процесс развёртывания уместился в один меленький bash-скрипт:

#!/bin/bash -ex

BRANCH=${1:-master};

mix edeliver build release --branch=BRANCH --verbose
mix edeliver deploy release to production --verbose
mix edeliver start production --verbose
mix edeliver migrate production up --verbose

Скорее всего Вам придётся подкрутить этот скрипт под собственные нужды. Мы используем его только для развёртывания в production, но Вы так же можете использовать его и для staging развёртываний. Описание того, как всё это работает — под катом.

Читать полностью »

Elixir: Регистрируем процессы — практическое руководство - 1

Процессы в Elixir (ну и в Erlang конечно же) идентифицируются с помощью уникального идентификатора процессаpid.
Мы используем их, чтобы взаимодействовать с процессами. Сообщения посылаются как бы в pid, а виртуальная машина сама заботится о доставке этих сообщений в правильный процесс.
Иногда, впрочем, чрезмерное доверие к pid может приводить к значительным проблемам.
К примеру, мы можем хранить pid уже мёртвого процесса, или мы можем использовать Supervisor, который абстрагирует создание процессов от нас, поэтому мы даже не знаем, какой у них pid (пер: а ещё Supervisor можете перезапустить упавший процесс с другим pid, и мы об этом не узнаем никак).
Давайте создадим простое приложение и посмотрим: с какими проблемами мы можем столкнуться и как мы эти проблемы будем решать.

Читать полностью »

Elixir: начинаем работу с Plug - 1

В мире Elixir, Plug представляет собой спецификацию, позволяющую различным фреймворкам общаться с различными web-серверами, работающими в Erlang VM.
Если вы знакомы с Ruby, то можете провести аналогию с Rack: Plug пытается решать те же проблемы, но только другим способом. Понимание основ работы Plug позволит лучше разобраться как с работой Phoenix, так и других web-фреймворков, созданных на языке Elixir.

Читать полностью »

Привет, коллеги!

Я расскажу о библиотеке для Питона с лаконичным названием f. Это небольшой пакет с функциями и классами для решения задач в функциональном стиле.

— Что, еще одна функциональная либа для Питона? Автор, ты в курсе, что есть fn.py и вообще этих функциональных поделок миллион?

— Да, в курсе.

Причины появления библиотеки

Читать полностью »

Элементы функционального программирования появились в Java сравнительно недавно, но приобретает все большую популярность. Особенно в части stream API – наверное нет Java разработчика, который бы не слышал/читал/применял этот API для работы с коллекциями. К сожалению, большинство и не идет дальше использования Stream API, тогда как функциональный подход позволяет значительно упростить жизнь разработчикам автотестов. Ниже я расскажу про два примера такого упрощения – словари проверок и специализированные матчеры

Читать полностью »

Приветствую! В этой статье будет показано, как, имея на руках обычные Future-ы, сделать в scala подобие корутин и асинхронные stream-ы. Этакий небольшой туториал по функциональному программированию.

Что это и зачем

Что такое Future человеческим языком

Future — это сущность, описывающая результат некоторых вычислений, который мы получим не сразу, но в будущем. Но есть одна особенность: зачастую мы, не зная еще результата, точно знаем, что мы с ним будем делать. Например, мы попросили у сервера какой-то конфиг, и теперь у нас есть Future[Config]. Сам конфиг мы еще не получили, но точно знаем, что, когда получим, то достанем из него адрес и по этому адресу попросим у сервера картинку (config => Future[Image]). И Future[Config] способна изменяться таким образом, чтобы мы вместо конфига и потом картинки могли получить сразу картинку. Сущности, способные комбинироваться таким способом, называются монадами.

К сожалению, простое последовательное комбинирование 2х и более асинхронных операций (загрузить конфиг, а потом картинку по адресу из конфига как пример) — это все, на что способны обычные Future-ы в качестве монад. Они не позволяют ни сохранять состояние, ни делать циклы из асинхронных операций, ни выдавать несколько (или бесконечно много) значений. Вот этими недостатками мы сейчас и займемся.

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

Применив знания из этой статьи, мы сможем этот процесс описать примерно так:

Код

// Про 'FState' - далее, пока же просто примем, что это - такая необычная Future
def getNextConfig: FState[Config]
def getTemperature(from: String): FState[Int]

case class State(temperature: Int, sumTemp: Long, count: Int) {
  def isGood = ...
}

// Как видим, получается единый асинхронный алгоритм с состоянием, 
// которое извне этого алгоритма не видно
val handle = 
  while_ ( _.isGood)
  {  for (
        config <- getNextConfig();
        if (config.isDefined);  // пустой конфиг - прекращаем выполнение
        nextValue <- getTemperature(config().source);  // грузим значение температуры
        state <- gets[State];  // тут мы берем текущее состояние
        newState = State(nextValue, state.sumTemp + nextValue, state.count + 1);
        _ <- puts(newState);  // .. и меняем его
        _ <- runInUiThread { drawOnScreen(newState) }
  ) yield() }

Или вот так:

Код

val configs: AsyncStream[Config] = ... // получаем откуда-то stream конфигов

def getTemperature(from: String): FState[Int]

case class State(temperature: Int, sumTemp: Long, count: Int)

// Получается то же самое, только вместо зависимости 'getNextConfig'
// мы, по сути, передаем сами данные - stream из конфигов
val handle = 
  foreach(configs) {
    config => for (
        nextValue <- getTemperature(config().source);  // грузим значение температуры
        state <- gets[State];  // тут мы берем текущее состояние
        newState = State(nextValue, state.sumTemp + nextValue, state.count + 1);
        _ <- puts(newState);  // .. и меняем его
        _ <- runInUiThread { drawOnScreen(newState) }
    ) yield()  
  }

Всех, кто заинтересовался, прошу под кат.
Читать полностью »

Развернутый ответ на вопрос, вынесенный в заглавие поста, приводится в статье Брюса Эккеля в редакции от 25 ноября 2015 года. Мы решили разместить здесь перевод этой статьи и поинтересоваться, что вы думаете о функциональном программировании в Java, а также об актуальности такой книги:

Лямбда-выражения Java 8 — это замыкания? - 1

Приятного чтения!

Читать полностью »

image

Что будет: доклады, пицца, общение, отличный вечер четверга.

Список заявленных докладчиков и тем под катом.
Читать полностью »


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js