Php => Scala: перестройка мозга

Я люблю статически типизированные языки. Начиная с Pascal/Delphi 8 лет назад, затем разрабатывая приложения на c#, в конце концов, однако, я перебрался на динамический php и веб-разработку, чем занимаюсь последние 4 года. PHP хорош для заработка, т.к. он сверхпопулярен и проектов в изобилии на фриланс-сайтах по рефакторингу, доработке, обновлении или создании новых веб-приложений на php. Я влюбился в PHP, в его простоту и наглядность, однако где-то в душе я искренне жажду работать с умным type-осведомленным компилятором и строгой типизацией. Поэтому периодически я просматриваю мануалы новых фреймворков, новых языков и статьи гиков типизированных языков - в надежде найти что-нибудь настолько вкусное, чтобы попробовать очередной проект не на PHP.

Последние месяцы я активно изучаю язык Scala (статически тпизированный, компилируемый в JVM bytecode, часто воспринимаемый как альтернативу языку Java). Бесспорно, работать с PHP все еще в разы проще в моих глазах, чем Java-like разработка, но мой мозг начинает переворачивать некоторые понятия в последнее время по ходу прочтения и изучения всего, что касается Java-world + Scala.

В этой статье я хочу поделиться своим опытом [не разработки] изучения мира Java/Scala, подготовки мозга для разработки под JVM. Тут я скомпоновал основные за и против, некоторое сравнение, ответы на вопросы, которые у меня возникали по ходу ввода, и может быть даже ощущения. Это ни в коем случае не холивар-статья, а скорее мой опыт борьбы привычки с разумом.

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

Scala?

Однажды я наткнулся на сайт языка Scala. Совсем случайно. Первое впечатление от прочитанных обзоров - опять Java, только с улучшеным синтаксисов. Конечно, эти мысли я забросил. Однако подписался на новости по ключу "scala language" в Google News и упорно читал. Со временем я начал понимать, что Scala - не Java. Это совершенно новый язык программирования, который пытается будучи полностью статически типизированным - оставаться простым в синтаксисе, как динамические языки, такие как PHP. Кроме того, идеология Scala предполагает очень развитый OOP, который легко используется в паре с функциональным программированием. Идея такого радикального (если громко сказано, читай "чистого") подхода меня окончательно покорила и следующие 2 недели я потратил на прочтение всей книги Programming in Scala, в создании которой учавствовал сам разработчик языка (сегодня уже доступна второая редакция этой книги). Что я вынес для себя на этапе прочтения книги, безотносительно веб-программирования на Scala:

  • Безальтернативная статическая типизация. То, что я хочу. Простыми словами это означает: компилятор (если он умный) отлавливает большинство ошибок кодинга (не бизнес-логики) еще на этапе компилирования.
  • Concise syntax, это слово часто употребляется в книге, что означает - компактный синтаксис. Хотя типизация обязательна абсолютно для всего, компилятор Scala дейсвительно умен настолько, что типы нужно указывать только там, где компилятор не может их предположить сам, и предполагает он их практически везде. Самый простой пример val x = "Some String" - ничем не отличается по сложности от объявления переменной в PHP, не требует указания типа String, однако x будет понята компилятором как строготипизированная переменная типа String.
  • Pattern Matching - это очень развитое подобие конструкции case, которое умеет извлекать частные случаи не только из значений, но и из типов, а также любой вложенности по иерархии объекта. Это означает, что в case-конструкции (в Scala это буде match) можно проверить тип/значение объекта, его полей, полей его полей и т.д., в объявлении проверки сразу присвоить переменные-альясы на любые проверяемые значение и использовать их в правой части проверки для необходимых инструкций.
  • Case Classes - позволяет осуществлять pattern matching с поддержкой проверки компилятором, что все возможные случаи охвачены в вашем разветвлении. Если Вы не охватили все возможные случаи, компилятор просто не скомпилирует исходный код.
  • Partial functions - простыми словами, вы можете создать из одной функции другую, применив к ней значения некоторых ее аргументов, но оставив другие неизвестными.
  • Traits - возможность выделять поведение в отдельные классы и миксовать (mixin) их в другие классы на любом уровне ООП, в любом количестве. Это кардинально отличается от иерархи классов (и натурально дополняет ее). Простой пример - trait Ordered. Если замиксовать его в класс и реализовать метод compare(), целый набор других методов сравнение появится доступным в создаваемом классе: <=, <, !=, >=, >
  • Все операторы - методы. Все функции - классы. Это отличное подспорье, чтобы создавать DSL - внутренние языки под конкретные бизнес-нужды.

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

JVM - ох как это неудобно для меня

Да, я до сих пор считаю, что это неудобно. Если сравнивать с любимым PHP.

Что нужно для того, чтобы начать работу на PHP? Минимум - один .php файл и настроенный сервер. Удобность не только в понятной и произвольной структуре, а и в том, что ничего не нужно компилировать, приложение state-less, и на практие это означает, что мы четко видим и знаем, что получим на выходе, мы знаем недлинный путь, по которому пройдется скрипт, и мы полностью владеем ситуацией, так сказать.

Что я могу сказать относительно JVM? Изобилие деплоинга не радует.

  • Существует очень много разных серверов, разных типов встраивания веб-приложения в страницу, и их нужно понять, чтобы выбрать. В PHP все ясно - обще используемый Apache или ngnix, центральная понятная документация и "любой друг подскажет", помимо гугла.
  • Компиляция - да, это неудобно. Нужны, проще говоря, специальные настройки или приспособления, чтобы менять код и по ходу видеть изменения в браузере.
  • XML-файлы настроек - их много, чтобы их редактировать - хочется разобраться, и на это уходит время. Ни в какое сравнение с одним php.ini и установкой нужных значений прямо в коде php.
  • Maven-подобные тулзы. Их нужно устанавливать и изучать. Они создают предопределенную структуру приложения, они должны быть понятыми вашими IDE (для этого нужны плагины), нужно понимать, какие файлы игнорить в репозитории кода, нужно знать, в каком xml-файле и как называется та или иная настройка.

Опытный гуру подскажет еще немало существенных различий, но мне с головой хватает нескольких перечисленных выше, чтобы с JVM работать не хотелось.

Выбор фреймворка

Кроме неприятностей с JVM, оказалось непросто выбрать фреймворк. Я прочитал несколько больших обсуждений на StackOverflow, погуглил и прочитал [да, практически полностью] все мануалы всех scala-ферймворков (или java с модулем для поддержки scala), полистал обсуждения в гугло-группах и вики-страницы, создаваемые пользователями этих фреймворков, и чем дальше - тем сложнее было на что-то решится.

Средние и мелкие фреймворки я в итоге откинул по нескольким причинам:

  1. Они решают некоторые задачи элегантно (субъективно, с точки зрения php-кодера), однако не решают другие задачи совсем.
  2. У них нет поддержки для наиизвестнейших Java-IDE, таких как NetBeans и IntelliJ IDEA.
  3. Они требуют дополнительных усилий для реализации hot swaping (когда меняешь код, а перекомпиляция происходит автоматически, в браузере остается нажать F5).
  4. Отзывчивость комьюнити очень низкая, тогда как мне (ламеру) понадобилась бы быстрая помощь специалистов с набитой рукой.
  5. Отсутствие бенчмаркинга, использование на боевых серверах и т.д. (моя же цель - написать следующий коммерческий проект на Scala).

Итак, я сосредоточился на выборе между Play! и Lift. Несмотря на неплохую документация Play! и полную неразбириху в туториалах Lift (сейчас уже с документацией у Lift немножечко лучше), я не спешил с выбором. Вначале я бегло прочитал компактные туториалы Play! и пролистал книгу Lift!. Однако это не помогло, все еще осталось множество вопросов, ответы на которые забрали у меня дни (или недели? кто уже помнит эти бессонные ночи...). Сравнительную итоговую сводку и впечатления привожу далее.

  1. Документация Play! - это клёво. Все ясно и компактно. Документация у Lift! это бардак: и в туториалах, и в книгах знания подаются непоследовательно (с точки зрения нуба в мире Java). Впрочем, чтение Play! не проясняет ситуации в Java, однако становится ясно, что за нас уже обо всем позаботились: скачал, запустил, работает. Прописывай роутинг в конфиге, пиши возвращаемый html. Все как в php (в частности, роутинг очень похож оный на F3). В Play никто не говорит про Maven или другие build tools, деплоить предлагают "как есть", и встроенный сервер, и ключенный hot swap обещают работать на отлично даже на серьезных нагрузка.
  2. Google Groups - тут все абсолютно наоборот. Мои вопросы в группе Play ожидали ответа нудно и долго. В группе Lift на 80% вопросов отвечают за несколько часов, и, что очень приятно, в дискуссию в 80% случаев ввязывается David (DPP) - главный идеолог и разработчик. При этом он не стесняется подбирать нужные ссылки и объяснять одно и то же разными словами в разных ветках разным людям.
  3. Поддержка Scala... В Play! меня действительно пугает то, что: a) на вопросы относительно Scala-модуля отвечают еще медленно б) модуль в версии 0.9, и roadmap до версии 1.0 вещает нетривиальные изменения в структуре контроллеров, роутинге и некоторых других штучках, что означает - придется переписывыть и понимать новые концепции заново.
  4. First View Templating - идеология Lift-а. Очень, очень непривычно для нас, PHP-шников, привыкших иметь один ответ на один HTTP-запрос и один шаблон на один action контроллера. Однако - многие технически статьи гиков настоятельно обосновывают в пользу First View - это скорее и больше всего касается интерактивности веб-приложения. Мне это в данный момент не нужно, но я не против попробовать, тут трудостей быть не должно, т.к. я фан PHPTAL, который является атрибутивным языком разметки, и переход на аналогичный в Lift не должен составить труда. Напротив, в Play язык разметки больше похож на Language-as-templating, то есть, сам язык используется для разметки (не люблю smarty-подобные разметки).
  5. В конце концов, Play - по функциональности является подмножеством Lift-а, к такому выводу я пришел, читая блоги гиков книгу Lift.

Как вы понимаете, на весах плавно перевышивает Lift (простите уж фаны хорошего Play). Однако тревожные мысли и сомнения все еще не дают мне остановится Lift, а именно: я не хочу разбираться в maven, я боюсь statefull, я хочу hot swapping, в конце концов я хочу нормальную документацию, и я хочу хорошую поддержку в моем любимом IDE IntelliJ (но так и быть, я не откажусь от другого IDE, только дайте мне что-то работающее).

Далее я расскажу о моих исследованиях в перечисленных вопросах, которые так меня треважат (некоторые и по сей момент).

Lift - starting, IDE, hot swapping

Сходу, сдуру начал я разбираться в Maven и SBT. Хотел все понять и разобраться, но какой же это дискомфорт для моих мозгов. Ужасно чувствуя себя в этих разбирательствах, я отложил Lift на еще 2-3 недели, сделал очередной проект на PHP, за это время снова замечтался о статической типизированности и о композиции классов с помощью Traits там и сям в коде... и это сподвигло меня набраться терпения и попробовать Lift заново. К этому времени вышла новая версия Scala, новая версия Lift, и начало было действительно приятным - чтение новых фич языка и фреймовка, их обновление, параллелно зарядка позитивом и вдохновением: "эх, сейчас начну, уже руки чешуться!".

Я по своему внутреннему содержанию не могу что-то использовать, не разобравшись в том, как оно устроено и работает. Но из страха затравить свой интерес сложностями, я решил на этот раз довериться [этим ненавистным мне] hello-world проектам. Ладно. Я скачал последние заготовки проектов Lift с официального сайта, выбрал тот, что HTML5, Blueprint (я фан YUI3, но я снова переступил через себя, в 3-ий раз это не так уж и досадно), с уже реализованной регистрацией пользователей, восстановлением пароля, авторизацией и меню. Далее, по их инструкциями, я cd в корневую папку проекта и набираю "sbt update ~jetty-run". Чорт, оно сразу работает! Как я хочу. Меняю первый попавшийся под руку шаблон, F5 в браузере - ... снова  работает. Я в восторге. На что я потратил прошлые дни контакта с Lift??? (Я наверное дурак или глупо подошел к этому делу в первый раз, но статья не об этом).

Еще 3 часа на вопрос "помогите мне нубу" в гугло-группе Lift, и я вдруг понимаю, что я хочу Lift, я настрою JVM под мои запросы коммерческого проекта, . Да, это приятная эмоция. Но я хочу позитива после всех этих мучений. И меня это радует. Еще 10 минут занимает чтение мануала к плагину к sbt (scala built tool, уже присутствует в заготовке проекта). 4 команды в cli и я получаю готовые файлы проекта IntelliJ IDEA. Дальше - не знаю, что еще сказать об этом экспериенсе, но одно об одной эмоции я не могу умолчать - ребята, действительно попробовать Lift это просто и быстро. Но только если вам просто закрыть глаза на то, что вы ничерта не понимаете, что и как происходит внутри, кто контролирует роутинг, как компонуются шаблоны, где база данных пользователей, и как же устроен hot swapping (да, я хочу понимать все, чем пользуюсь, и это мне мешает следовать инструкциями do-it-1-2-3).

Stateless против Stateful

Следующая, наверное самая необычайно тяжелая бороба в моих мозгах это stateless vs statefull. О чем, собственно, спор?

Когда вы, в PHP, обрабатываете HTTP-запрос и формируете ответ в виде, например, HTML или JSON или XML - необходимо взять из кукисов токен сессии, проверить, загрузить по токену переменные сессии, загрузить авторизованного пользователя из базы и произвести некоторую другую инициализацию. Такой подход называется stateless - т.к. никакие объекты не сохраняются инициализированными в памяти между HTTP-запросами. С одной стороны - это хорошо по некоторым причинам. Во-первых, присутствует полный контроль происходящего, это интуитивно понятно, мы видим и чувствуем последовательность происходящего при обработке запрос с нуля и до формирования ответа. Во-вторых, выделяемая память периодически освобождается после обработки ответа. В третьих - можно дублировать инстансы веб-приложения на многие серверы, подсунуть им одну базу, и таким образом легко реализовать горизонтальное масштабирование системы (то есть, увеличение количества харда вместо улучшения качества софта, скорости харда и рефакторинга кода в "узких" местах).

Те, кто предпочитают stateless модель, конечно же, оперируют сильными аргументами в свою пользу, которые я только что перечислил. Stateful - это неоправданно сложно, говорим мы, это не нужно, в конце коцов stateless это интуитивно и соответствует RESTful application. Однако все же необходимо откинуть свое фе (к чему я долго шел) и наконец разобраться в stateful, его преимуществах и недостатках, и отвить для себя на простой вопрос - почему там, на той стороне, довольные и самовлюбленные гики критикают наш stateless в пух и прах, и остаются довольными и, заметьте, продуктивными в своей работе в Java world. Не легко было начать вникать без предвзятости, но, кажется, мне удалось поверить в stateful, в основном - благодаря одной, главной, статье. Я не засоряю ссылкам этот пост (все ссылки приведены внизу), однако не удержусь всунуть таки одну ссылку прямо в текст: http://lift.la/lift-state-and-scaling - это главное, что я рекомендую прочитать всем, кто еще на PHP, но руки чешуться попробовать Scala/Lift.

Итак, что же такого магического в stateful?

  • Первый простейший пример - инициализация сессии. Загрузка пользователя из базы данных происходит один раз. Десериализации сессионных данных не происходит и вовсе при каждом HTTP запросе, а значит - мы экономим на io-операциях (ценой RAM, но не копеечная ли это плата), соответственно, можем обслужить больше одновременных http запросов с одной машины.
  • Второй простейший пример - мы можем хранить "сколько записей отображать вот на этой странице для этого пользователя, и как фильтровать и сортировать" - в синглтоне, не нужно никуда это сохранять, это значение просто есть в памяти и доступно в коде, им манипулировать. Снова, экономия на IO.
  • В указанной выше статье также наткнемся на объяснение того, что stateful намного (!!) надежнее и менее подвержен различным способам взлома, там же - ссылка на top способов взлома и аргументацию, почему это невозможно с Lift и stateful.
  • Comet / WebSockets / Actors. Тут не место объяснять эти технологии, однако все сводится к простой реализации динамических веб-сайтов в стиле программирования программы на c# или VisualBasic (lol). stateful позволяет абстрагироваться от http-запросов на высоком уровне и сконцентрироваться на полезном коде, позволяет обновлять части веб-страницы без дополнительных специальных "приспособлений" (как бы сказать, нативно).
  • Это все еще легко поддается горизонтальному масштабированию! По той же ссылке - детальные объяснения и обоснование.
  • Кто сказал, что следует бояться разрыва соединения? Если вам это говорят, то только от непонимания / незнания того, что обновление к клиенту (в браузер) приходит пачками в нужном порядке, тогда, когда появляется коннект к серверу. Собственно, никаких проблем. Нечего бояться. Все продолжает работать как и со stateless.
  • Цель этого списка - вовсе не полностью объяснить почему stateful это даже очень неплохо. Цель - заставить вас сомневаться внутри самих себя, и наконец пойти и прочитать внимательно ту статью.

Итак, что дальше? Я не стал приверженником stateful и ярым ненависником stateless. Однако теперь я ясно понимаю, что нечего бояться statefull-модели. Это моя проблема, если я не хочу понять, как пользоваться этим подходом. Но многие огромные и успешные сайты пользуются этой моделью, легко скалируются и обслуживают огромное количество клиентов без проблем. Значит, проблема не в подходе, а в моей голове, которая предвзято к ней относится. Таким образом, я остановился на том, что мой внутренний мир не пострадает от перехода на stateful в Lift, и я не боюсь никаких там 100 хитов в секунду, которых вряд-ли достингу даже в течении года с моим коммерческим веб-сайтов средней тяжести (чур хабра-эффект не считается, но я думаю и это не заглушит движок).

Внутренний конфликт PHP-мозга все еще не исчерпан

Я захотел попробовать Lift, точнее, я уже его пробую. Я поверил в statefull. Я перестал переживать за JVM и деплоинг. Я до сих пор не вникал и не хочу в то, как работают Maven или SBT, т.к. пару простых команд решают все, что нужно для начала разработки. И в то же время - я получил доступ к возможности попробовать Scala (ути-пути, мой хорошенький) - последовательно и не конфликтуя с моим складом ума, подходом к разработке веб разработке, и не боясь небольших [надеюсь] сложностей, ожидающих меня по настройке боевого сервера и деплоингу.

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

  1. Документация Lift - это все еще бардак. Настолько бардаковый, что нужно читать много и во многих местах, чтобы понять, как оно устроено. Хочется создать последовательную документацию, даже пусть и на русском. Но на это, конечно же, нету времени.
  2. ORM - как она работает в Lift? Где эта дефолтовая схема базы, я так и не нашел ее? Где о ней что-нибудь написано? Может она сама создается? Даже нету в гуглогрупс. По всей видимости, это будет мой следующий вопрос туда.
  3. Чтение группы Lift в гуглодоксах. Практика показывает, что даже если пока что все ясно, нужно читать. Там частенько вступает в диалог автор фреймворка, читая которые, начинаешь по-другому понимать то, что раньше казалось и так понятным.
  4. Что правильно заливать в репозиторий, а что игнорить?
  5. Как правильно организовать конфигурацию, чтобы не заливать ее в репозиторий и настраивать per-server.
  6. Как автоматизировать deploying.
  7. Как жить без нативной поддержки Lift-а в IntelliJ IDEA. Нет, я не о парсинге языка Scala, я о абстракциях (системе шаблонов  и других) в глазах IDE. Поддержка же языка Scala в IDEA уже на топовом уровне, о чем свидетельствуют слюни из уст гиков от слишком широкой довольной улыбки )

Не знаю, чем закончить этот монолог. По всей видимости, продолжение следует...

Подборка почитать