Решил немного отвлечься от технического образования и заняться научно-популярной литературой. Был приятно удивлён простотой и доступностью книги вышедшей в оригинале на русском да ещё и написанной человеком работающим в Москве. Работа всё ещё не попадает в разряд лёгкого чтива, требует определённой концентрации и запоминания терминологии, но тем не менее доступна без специальной подготовки и даже приятна для людей со склонностью к разбиранию сложных штук.
Книга посвящена современному состоянию эволюционной биологии, в особенности тому что известно о ранних этапах развития жизни. Попутно делается краткий экскурс в современное состояние генетики и молекулярной биологии, которые изрядно продвинулись со времён написания учебников по которым я учился. Много врезок кратко описывающих результаты отдельных исследований конца 2010х годов и их место среди остального материала. Как и положенно научной книге большинство упомянутых фактов сопровождаются ссылками на соответствующие работы.
Медленно и неспешно, начиная с самых простых организмов автор рассматривает как устроен и мог возникнуть соответствующий тип. Начало, конечно, получается довольно насыщенным гипотетическими формами жизни, но это, по мере приближения во времени к современности а по уровню организации к современным многоклеточным и их сообществам, проходит. Приводится множество фактов палеонтологии и недавних открытий реликтовых видов, про которые неспециалисты вряд ли слышали. Огромный массив информации представляется как логически связанная цепочка фактов.
Неожиданно подробно для книги с такой направленностью разбираются химические механизмы регуляции в живых организмах, современное состояние молекулярной биологии. Все даётся в описательном виде, без технических деталей, что может быть небольши минусом для людей не страдающих страхом перед химией. Где уместно полностью рассматриваются цепочки обратной связи от гена до влияния на внешнюю среду и обратно, приводится несколько "интуитивных" доказательств что даже самые простые обратные связи могут эволюционировать в сторону усложнения и адаптации. Вообще одна из позитивных идей вынесенных мной из книги - математические модели и формальные доказательства становятся всё более важной частью биологии.
В общем книга оставляет в голове достаточно яркую и развёрнутую картину современной биологии и её взгляда на историю жизни. Бонусом читатель знакомится с существенно более детальным описанием микроскопического устройства клетки и её механизмов регуляции чем остаётся от школьного курса биологии. В целом дочитывал книгу в состоянии легкого восторга и немедленно приобрёл ещё одни труд автора, ждите ещё обзоров.
среда, 4 февраля 2015 г.
среда, 12 ноября 2014 г.
Линкопост #3
Понял что умных своих мыслей не было уже пару месяцев и не предвидится. Давайте хоть чужими поделюсь чтобы блог не умер.
Для начала про хаскель и теорию:
Crash Course on Notation in Programming Language Theory - название говорит само за себя. Позволяет не пропускать разделы с формальной семантикой в современных статьях по теории ЯП. В принципе материал достаточно уникальный, я например больше нигде не видел описания этой нотации.
Также наткнулся на две статьи с нормальным описанием подхода к IO в Haskell. First-Class “Statements” и I/O Is Pure. Они наглядно показывают как устроен ввод-вывод в ленивых языках. Что такое тип IO и каков смысл основных операций с ним в терминах "read data" и "apply to result" а не выдуманнных эффектов и действий. Как небольшой побочный результат дают возможность понять что монады, функторы и прочие интересности которыми забиты все тьюториалы и ответы на SO никакого отношения к сути IO не имеют.
Нашлось за полгода и много интересного и про развитие языков программирования.
Всплыла демо-сессия посвящённая дебаггеру в Elm, Bret Victor style reactive debugging. Сейчас этот проект уже влит в основную ветку разработки и доступен как reactor.
Chris Granger написал пару стоящих прочтения статей о том что он дальше собирается делать с IDE. Первая, Toward a better programming, неплохо рассмотрено почему софт пишется медленно и заброшена пара идей что с этим можно сделать. Также было сделано объявление о старте EVE, по делу там достаточно мало, но всё-таки читать интересно. Фактически признано что Light Table превратился в "ещё один редактор" с парой секси фич. Чтож светлая ему память, у EVE есть все шансы таки сдвинуть идею "среды разработки" с мёртвой точки.
Интересная декларация создания двухуровневого языка программирования. Я так понял чистое прожектёрство, по проекту год не было новостей, но идея выглядит неплохо Big Programming, Small Programming.
Небольшой флешбэк, The Mother of All Demos. Презентация руководителя ARC о результатах работы лаборатории 1968 года. Показывается ряд интерактивных систем и озвучиваются идеи их развития. Пользуясь случаем хочу передать пламенный привет всем свидетелям Джобса придумавшего ПК.
Отдельно стоит найденная мной серия лекция Хэмминга (того самого) Learning to Learn. Название говорит само за себя, Хэмминг рассказывает о том как он учился сам и советует как учиться студентам. Особого внимания достойна завершающая лекция You and Your Research.
Для начала про хаскель и теорию:
Crash Course on Notation in Programming Language Theory - название говорит само за себя. Позволяет не пропускать разделы с формальной семантикой в современных статьях по теории ЯП. В принципе материал достаточно уникальный, я например больше нигде не видел описания этой нотации.
Нашлось за полгода и много интересного и про развитие языков программирования.
Всплыла демо-сессия посвящённая дебаггеру в Elm, Bret Victor style reactive debugging. Сейчас этот проект уже влит в основную ветку разработки и доступен как reactor.
Chris Granger написал пару стоящих прочтения статей о том что он дальше собирается делать с IDE. Первая, Toward a better programming, неплохо рассмотрено почему софт пишется медленно и заброшена пара идей что с этим можно сделать. Также было сделано объявление о старте EVE, по делу там достаточно мало, но всё-таки читать интересно. Фактически признано что Light Table превратился в "ещё один редактор" с парой секси фич. Чтож светлая ему память, у EVE есть все шансы таки сдвинуть идею "среды разработки" с мёртвой точки.
Интересная декларация создания двухуровневого языка программирования. Я так понял чистое прожектёрство, по проекту год не было новостей, но идея выглядит неплохо Big Programming, Small Programming.
Небольшой флешбэк, The Mother of All Demos. Презентация руководителя ARC о результатах работы лаборатории 1968 года. Показывается ряд интерактивных систем и озвучиваются идеи их развития. Пользуясь случаем хочу передать пламенный привет всем свидетелям Джобса придумавшего ПК.
Отдельно стоит найденная мной серия лекция Хэмминга (того самого) Learning to Learn. Название говорит само за себя, Хэмминг рассказывает о том как он учился сам и советует как учиться студентам. Особого внимания достойна завершающая лекция You and Your Research.
Ну и немного по управлению, статья в духе капитана, но так нереально трудно усваиваемая людьми работающими в менеджменте Банальность: главный ингредиент в разработке ПО -- это люди.
понедельник, 11 августа 2014 г.
Опыт с Bullet Journal
Какое-то время назад я увидел сам и немного порекламировал окружающим аналоговую систему ведения списка дел: Bullet Journal. Суть кратко: автор предлагает типографскую нотацию легко воспроизводимую от руки для управления списками дел на бумаге без помощи компьютера.
Сама идея весьма удобна. Во-первых аналоговая запись существенно расширяет диапазон артефактов которые можно хранить, в программировании часто нет слов зато есть небольшая схемка. Во-вторых записи можно обрабатывать и дополнять оффлайн, в вагоне поезда, в палатке за городом, когда лежишь на диване и боишься случайно засветиться в скайпе.
Однако некоторые детали этой системы меня не устроили и в процессе использования прижился ряд поправок. Последняя версия не подвергалась изменениям уже месяца 3, так что я решил ей поделиться.
Крупнейшее изменение которое я внёс состоит в разделении журнала на две книги. Одну для "текучки" и одну для "вечного". Первую я веду в блокноте чуть меньше А5, туда идут собственно кратковременные задачи и малозначимые заметки по разным топикам. Вторую я веду в обычной школьной тетради. Туда идут долговременные планы, эскизы по разным проектам, важные мысли. Обе книги оформлены как оригинальный журнал: содержат нумерацию страниц и оглавление.
Второе важное отличие: удаление контента. Меня существенно раздражает наличие в поле зрения ненужных вещей, в частности топиков которые уже точно не актуальны. И если оригинальный журнал был append-only, то мои книги подразумевают удаление. В книге с текучкой я просто вычёркиваю элементы оглавления и вырываю соответствующие страницы когда топик становится полностью закрыт или не актуален. Из книги с вечным я только вычёркиваю названия топиков как в оглавлении так и на странице.
Небольшим изменением подвергся набор иконок. Значок "звезда" долго рисовать и редко получается если пишешь на коленях. Иконка priority была заменена на восклицательный знак, соответственно для inspiration я использю простую молнию. Теперь все значки рисуются в пару касаний и узнаваемы даже когда добавлены в вагоне метро.
Я полностью не отказался от цифрового хранения тудушек. Для трекинга живого и эволюционирующего проекта оно подходит лучше. Ну и хранить ссылки на бумаге немного не удобно. Для таких задач я использую Trello. Топики иногда перемещаются между журналом и облаком. Для сохранения разума были выработаны два правила. Первое: никакой топик не живёт одновременно в двух системах. Второе: никогда не осуществлять перенос сразу, я всегда ставлю специальную пометку в названии и осуществляю перенос только когда на неё натыкаюсь.
Система в общем оказалась весьма полезной и после небольшого тюнинга достаточно удобной. Иногда приходится выделять полчаса на обзор текущего состояния журнала и чистку перенос части содержимого, но случается это примерно раз в два месяца. При том что я регулярно оказываюсь поездах, автобусах и т.п. без особого желания читать но с желанием о чём-то подумать бумажный журнал оказывается весьма кстати. Основная проблема не забыть книги дома, но надёжного способа борьбы с ней пока не придумано.
Сама идея весьма удобна. Во-первых аналоговая запись существенно расширяет диапазон артефактов которые можно хранить, в программировании часто нет слов зато есть небольшая схемка. Во-вторых записи можно обрабатывать и дополнять оффлайн, в вагоне поезда, в палатке за городом, когда лежишь на диване и боишься случайно засветиться в скайпе.
Однако некоторые детали этой системы меня не устроили и в процессе использования прижился ряд поправок. Последняя версия не подвергалась изменениям уже месяца 3, так что я решил ей поделиться.
Крупнейшее изменение которое я внёс состоит в разделении журнала на две книги. Одну для "текучки" и одну для "вечного". Первую я веду в блокноте чуть меньше А5, туда идут собственно кратковременные задачи и малозначимые заметки по разным топикам. Вторую я веду в обычной школьной тетради. Туда идут долговременные планы, эскизы по разным проектам, важные мысли. Обе книги оформлены как оригинальный журнал: содержат нумерацию страниц и оглавление.
Второе важное отличие: удаление контента. Меня существенно раздражает наличие в поле зрения ненужных вещей, в частности топиков которые уже точно не актуальны. И если оригинальный журнал был append-only, то мои книги подразумевают удаление. В книге с текучкой я просто вычёркиваю элементы оглавления и вырываю соответствующие страницы когда топик становится полностью закрыт или не актуален. Из книги с вечным я только вычёркиваю названия топиков как в оглавлении так и на странице.
Небольшим изменением подвергся набор иконок. Значок "звезда" долго рисовать и редко получается если пишешь на коленях. Иконка priority была заменена на восклицательный знак, соответственно для inspiration я использю простую молнию. Теперь все значки рисуются в пару касаний и узнаваемы даже когда добавлены в вагоне метро.
Я полностью не отказался от цифрового хранения тудушек. Для трекинга живого и эволюционирующего проекта оно подходит лучше. Ну и хранить ссылки на бумаге немного не удобно. Для таких задач я использую Trello. Топики иногда перемещаются между журналом и облаком. Для сохранения разума были выработаны два правила. Первое: никакой топик не живёт одновременно в двух системах. Второе: никогда не осуществлять перенос сразу, я всегда ставлю специальную пометку в названии и осуществляю перенос только когда на неё натыкаюсь.
Система в общем оказалась весьма полезной и после небольшого тюнинга достаточно удобной. Иногда приходится выделять полчаса на обзор текущего состояния журнала и чистку перенос части содержимого, но случается это примерно раз в два месяца. При том что я регулярно оказываюсь поездах, автобусах и т.п. без особого желания читать но с желанием о чём-то подумать бумажный журнал оказывается весьма кстати. Основная проблема не забыть книги дома, но надёжного способа борьбы с ней пока не придумано.
понедельник, 4 августа 2014 г.
"Mathematics for Computer Graphics" и "Computational Geometry: Algorithms and Applications"
Последние полгода очень сильно ленился в плане чтения и справился всего с двумя книжками.
Первая "Mathematics for Computer Graphics" - краткое ревью математики полезной для программирования графики. Книга в принципе неплоха как напоминание и как компактная коллекция полезных математических инструментов. В качестве книги для начального изучения чего либо она к сожалению не очень хороша по нескольким причинам.
Во-первых книге катастрофически не достаёт иллюстративного материала, что фатально сказывается на скорости начального восприятия. Ко многим выкладкам и доказательствам приходится рисовать картинки самостоятельно, что исключает чтение книги в транспорте или на отдыхе.
Вторая проблема: разная глубина изложения в разных разделах. Какие-то направления представлены подборкой основных формул и их интерпретацией. Где-то есть пара примеров их применения, где-то вдруг приводятся доказательства. При сквозном чтении очень тяжело зацепиться за какой-то ритм и следовать ему.
В общем книга приемлема для полки в качестве справочника и не может использоваться для начального ознакомления с мат. аппаратом.
Вторая, "Computational Geometry: Algorithms and Applications". Книга как раз противоположна предыдущей - она очень удачна в качестве учебника. Начинает с простого, продолжает непростым, заканчивает комбинацией ранее изложенного. Очень приятной деталью является отличное покрытие иллюстрациями. Все логические построения и алгоритмы сопровождаются удачными рисунками - мне практически не приходилось брать карандаш и бумагу в руки.
Рассматривается около полутора десятков проблем вычислительной геометрии. Каждая атакуется плавно. Нет хороших решений из коробки, всегда показывается эволюция алгоритма от простых идей и игнорирования предельных случаев до доказуемо корректного решения. Авторы часто приводят не лучший алгоритм предпочитая детально описать более простой и понятный.
Чтение не требует знаний выходящих за первый семестр курса алгоритмов. Используются самые базовые вещи: деревья, кучи, списки. Анализ алгоритмов предполагает только знакомство с нотацией асимптот и умение решать "recurrences".
Все главы сопровождаются небольшим ревью вопроса. Там даётся история изучения проблемы и наиболее сильные современные результаты. Часто есть обзор смежных областей, приложений и т.п. Список ссылок огромный.
Книга даёт приятный уровень нагрузки на мозг, каждая задача требует 10-15 сконцентрированных размышлений сверх времени на прочтение. Можно рекомендовать и как учебник если вдруг пришлось столкнуться с геометрическими проблемами так и для развивающего чтения. В целом книга мне исключительно понравилась.
Первая "Mathematics for Computer Graphics" - краткое ревью математики полезной для программирования графики. Книга в принципе неплоха как напоминание и как компактная коллекция полезных математических инструментов. В качестве книги для начального изучения чего либо она к сожалению не очень хороша по нескольким причинам.
Во-первых книге катастрофически не достаёт иллюстративного материала, что фатально сказывается на скорости начального восприятия. Ко многим выкладкам и доказательствам приходится рисовать картинки самостоятельно, что исключает чтение книги в транспорте или на отдыхе.
Вторая проблема: разная глубина изложения в разных разделах. Какие-то направления представлены подборкой основных формул и их интерпретацией. Где-то есть пара примеров их применения, где-то вдруг приводятся доказательства. При сквозном чтении очень тяжело зацепиться за какой-то ритм и следовать ему.
В общем книга приемлема для полки в качестве справочника и не может использоваться для начального ознакомления с мат. аппаратом.
Вторая, "Computational Geometry: Algorithms and Applications". Книга как раз противоположна предыдущей - она очень удачна в качестве учебника. Начинает с простого, продолжает непростым, заканчивает комбинацией ранее изложенного. Очень приятной деталью является отличное покрытие иллюстрациями. Все логические построения и алгоритмы сопровождаются удачными рисунками - мне практически не приходилось брать карандаш и бумагу в руки.
Рассматривается около полутора десятков проблем вычислительной геометрии. Каждая атакуется плавно. Нет хороших решений из коробки, всегда показывается эволюция алгоритма от простых идей и игнорирования предельных случаев до доказуемо корректного решения. Авторы часто приводят не лучший алгоритм предпочитая детально описать более простой и понятный.
Чтение не требует знаний выходящих за первый семестр курса алгоритмов. Используются самые базовые вещи: деревья, кучи, списки. Анализ алгоритмов предполагает только знакомство с нотацией асимптот и умение решать "recurrences".
Все главы сопровождаются небольшим ревью вопроса. Там даётся история изучения проблемы и наиболее сильные современные результаты. Часто есть обзор смежных областей, приложений и т.п. Список ссылок огромный.
Книга даёт приятный уровень нагрузки на мозг, каждая задача требует 10-15 сконцентрированных размышлений сверх времени на прочтение. Можно рекомендовать и как учебник если вдруг пришлось столкнуться с геометрическими проблемами так и для развивающего чтения. В целом книга мне исключительно понравилась.
четверг, 19 июня 2014 г.
Что не так с Хаскелем
Недавно тут вспомнилась забавная статья "Почему язык Haskell так непопулярен" от одного из вождей соответствующего сообщества. Понял что со времён её публикации мой ответ на заголовок изменился с абстрактного "нихрена не понял" на список из нескольких достаточно конкретных проблем. Их я и попробую тут расписать.
Во-первых нет учебников и справочников по алгоритмам/структурам данных. Большая часть алгоритмов описана в расчёте на дешёвое присваивание и строгие вычисления. Найти описание и анализ нетривиального алгоритма для хаскельных реалий практически не возможно. У того же Окасаки, как я понимаю, половина книги - разработка аппарата для того чтобы только научиться иметь с этим дело.
К предыдущей плотно примыкает проблема номер два. Ленивые вычисления - это фактически полная передача компилятору баланса между потреблением памяти и процессора. Иногда он сдвигает его в совершенно неожиданное место и программа вдруг начинает требовать феерические объёмы ресурсов. Что с этим делать непонятно, гайды которые мне удавалось находить написаны для разрабов этого самого компилятора. В них в принципе нет введений, даже нет ссылок, предполагается что огромная масса знаний о реализации вычислительной модели уже в голове. Что делать человеку который прочитал LYHGG, написал первый алгоритм и получил время выполнение или потребление памяти в районе лунной орбиты непонятно.
Лирическое отступление. Пару лет назад я проходил на курсере курс по алгоритмам. Начать попробовал на хаскеле. Первым заданием было посчитать inversions, то есть нарушения порядка в массиве. Решение я написал сравнительно быстро, мин 40. Проблемы начались при запуске - программа пожирала всю доступную память. Несколько ритуальных плясок в обходе списка позволили программе вмещаться в пару гигабайт памяти и 20 минут. И да, выдавать корректный ответ для пары тысяч чисел в первой версии, спустя полтора часа...
Обзор тематических ресурсов подходы к решению проблемы найти не позволил. Консультация с коллегой-фаном хаскеля, который кстати тоже получил что-то не вменяемое в первой версии программы, и перестановка чего-то там в паре выражений (перед глазами встал призрак 2003 студии где перестановка инклудов иногда чинила access violation) позволили сократить время до нескольких минут. Для интереса сделал тоже на питоне за 10 мин, 20 или 30 секунд выполнения и символическое потребление памяти. Искать с помощью Хаскеля минимальный разрез графа из нескольких тысяч узлов я уже не рискнул.
Ну и главное: нет примеров полномасштабных приложений. Какая нибудь обработка исключений от трёх функций с помощью стрелок находится легко. Исходник простого веб-сервера, без расширений языка, без 90% закопанного в библиотеки найти сложно. Хотелось бы увидеть простой аналог рисовалки или текстового редактора. Что угодно что демонстрирует в рамках 3-4 экранов дизайн полноценного приложения которое читает, пишет, показывает что-то пользователю, реагирует на ошибки и нарушения форматов. Таких примеров нет, невозможно оценить какой массив знаний необходим для создания полноценного приложения. Теркат? Трансформеры? Дисер Окасаки? Свободная навигация по исходникам компилятора? Я за три года так и не понял.
Ещё для меня серьёзной проблемой является типографический кретинизм большинства авторов. Он проявляется в нескольких аспектах. Во-первых для функции нормально не иметь человеческого имени. Пишем <*>, в уме проговариваем "треугольна жопа", как описать код коллеге не понятно. Во-вторых статьи абсолютно всех уровней, от ICFP до очередного тьюториала по монадам в блоге программиста Васи наполнены типографскими аналогами собственно операторов языка, то есть вместо -> везде →. Какая-нибудь тау с нижним и верхним индексом - тоже святое дело. Вообще обучение на примере кода который не запускается - норма для данного сообщества. В том же LYHGG процентов 30 не работало ещё три года назад когда я его читал.
Ну и сообщество. Я там выше давал ссылочку на любителя корректных программ, подпишитесь - понаблюдайте недельку. Подумайте что делать если к такому существу придётся обращаться за поддержкой или оно окажется у вас в команде. Справедливости ради надо заметить что те кто собственно что-то делает, разработчики самого компилятора и популярных билиотек более вменяемы и не лишены кругозора.
Чуть не забыл, данная заметка не претендует на полный охват предмета. Haskell имеет некоторое (конечное) количество положительных черт, но я тут про них умолчал. Жаждущие серебряных пуль могут найти их в интернете во множестве.
UPD: Буду сюда докидывать пруфы
Во-первых нет учебников и справочников по алгоритмам/структурам данных. Большая часть алгоритмов описана в расчёте на дешёвое присваивание и строгие вычисления. Найти описание и анализ нетривиального алгоритма для хаскельных реалий практически не возможно. У того же Окасаки, как я понимаю, половина книги - разработка аппарата для того чтобы только научиться иметь с этим дело.
К предыдущей плотно примыкает проблема номер два. Ленивые вычисления - это фактически полная передача компилятору баланса между потреблением памяти и процессора. Иногда он сдвигает его в совершенно неожиданное место и программа вдруг начинает требовать феерические объёмы ресурсов. Что с этим делать непонятно, гайды которые мне удавалось находить написаны для разрабов этого самого компилятора. В них в принципе нет введений, даже нет ссылок, предполагается что огромная масса знаний о реализации вычислительной модели уже в голове. Что делать человеку который прочитал LYHGG, написал первый алгоритм и получил время выполнение или потребление памяти в районе лунной орбиты непонятно.
Лирическое отступление. Пару лет назад я проходил на курсере курс по алгоритмам. Начать попробовал на хаскеле. Первым заданием было посчитать inversions, то есть нарушения порядка в массиве. Решение я написал сравнительно быстро, мин 40. Проблемы начались при запуске - программа пожирала всю доступную память. Несколько ритуальных плясок в обходе списка позволили программе вмещаться в пару гигабайт памяти и 20 минут. И да, выдавать корректный ответ для пары тысяч чисел в первой версии, спустя полтора часа...
Обзор тематических ресурсов подходы к решению проблемы найти не позволил. Консультация с коллегой-фаном хаскеля, который кстати тоже получил что-то не вменяемое в первой версии программы, и перестановка чего-то там в паре выражений (перед глазами встал призрак 2003 студии где перестановка инклудов иногда чинила access violation) позволили сократить время до нескольких минут. Для интереса сделал тоже на питоне за 10 мин, 20 или 30 секунд выполнения и символическое потребление памяти. Искать с помощью Хаскеля минимальный разрез графа из нескольких тысяч узлов я уже не рискнул.
Ну и главное: нет примеров полномасштабных приложений. Какая нибудь обработка исключений от трёх функций с помощью стрелок находится легко. Исходник простого веб-сервера, без расширений языка, без 90% закопанного в библиотеки найти сложно. Хотелось бы увидеть простой аналог рисовалки или текстового редактора. Что угодно что демонстрирует в рамках 3-4 экранов дизайн полноценного приложения которое читает, пишет, показывает что-то пользователю, реагирует на ошибки и нарушения форматов. Таких примеров нет, невозможно оценить какой массив знаний необходим для создания полноценного приложения. Теркат? Трансформеры? Дисер Окасаки? Свободная навигация по исходникам компилятора? Я за три года так и не понял.
Ещё для меня серьёзной проблемой является типографический кретинизм большинства авторов. Он проявляется в нескольких аспектах. Во-первых для функции нормально не иметь человеческого имени. Пишем <*>, в уме проговариваем "треугольна жопа", как описать код коллеге не понятно. Во-вторых статьи абсолютно всех уровней, от ICFP до очередного тьюториала по монадам в блоге программиста Васи наполнены типографскими аналогами собственно операторов языка, то есть вместо -> везде →. Какая-нибудь тау с нижним и верхним индексом - тоже святое дело. Вообще обучение на примере кода который не запускается - норма для данного сообщества. В том же LYHGG процентов 30 не работало ещё три года назад когда я его читал.
Ну и сообщество. Я там выше давал ссылочку на любителя корректных программ, подпишитесь - понаблюдайте недельку. Подумайте что делать если к такому существу придётся обращаться за поддержкой или оно окажется у вас в команде. Справедливости ради надо заметить что те кто собственно что-то делает, разработчики самого компилятора и популярных билиотек более вменяемы и не лишены кругозора.
Чуть не забыл, данная заметка не претендует на полный охват предмета. Haskell имеет некоторое (конечное) количество положительных черт, но я тут про них умолчал. Жаждущие серебряных пуль могут найти их в интернете во множестве.
UPD: Буду сюда докидывать пруфы
- Need help - my haskell code is over 50 times slower than equivalent perl implementation
- The reasons I don’t write all my code in Haskell
- Where is Haskell going in industry?
- What's the performance bottleneck in this prime sieve function?
- Beginner - Parse Error on Input '='
- How do you avoid the Cabal Hell™? (третий пункт прекрасен, изящный функциональный дизайн типа)
- Complete roadmap from total novice to Haskell mastery?
- How do you structure a program to support logging?
- Why is foldl bad?
понедельник, 26 мая 2014 г.
Нетипичные IDE
Не так давно я написал довольно обстоятельный пост про проблемы современных IDE. В частности подробно отписался почему они могут нанести существенный ущерб проекту без должной осторожности и почему заметной пользы они не приносят. Что характерно пост получился самым популярным за историю блога и много народу не поленилось даже отстоять любимый инструмент в твиттере.
Пришло время перейти к конструктиву и немного посмотреть как их можно делать среды которые что-то меняют в процессе разработки. Есть изрядное количество инструментов которые пытаются продвинуться за пределы 95го года и существенно изменить способ взаимодействия программиста и разрабатываемой им программы.
Вот например Squeake, первый релиз 96 год. (Небось думали я с потолка год в прошлом посте взял? Правильно думали на самом деле, да и большинство обсуждаемых далее фич были в Smalltalk-80). Я наверно повторяю очень известную вещь, но всё таки: основной чертой Smalltalk является существование live image. В нём нет статического кода и времени исполнения, всё что делает программист порождает объекты в одном образе, этот образ несёт в себе и код и живые объекты на одинаковых правах. Образ содержит и ядро языка, и разрабатываемое приложение, и среду разработки одновременно, при поставке пользователю ненужные классы удаляются.
Подход этот конечно спорный, но он имеет несколько явно положительных результатов. Например при разработке графики можно весьма просто с ней управляться. Например имея нарисованную как-то картинку можно немного её покрутить, примерить к другой картинке, продублировать. Как-то так или так (вторая ссылка покороче, но требует скачивания видяшки). А после соответствующего благоустройства среда позволяет что-то программировать даже детям. Ну и вообще она полна простых приятностей, вот например Object inspector.
"Чего же тут такого интересного?" - многие спросят. Вроде и ничего, но вот 2014 год на дворе а все популярные среды разработки по-прежнему считают что цвет нормально показывать как десятизначное число.
Второй интересной наработкой Smalltalk является method finder. Первая его фича - поиск по ключевым словам более менее освоена современными IDE. А вот вторая, поиск по примеру и сейчас, спустя примерно 30 лет после создания насколько я знаю ни кем не повторена. Если кратко она позволяет ввести пример результата метода при заданных аргументах и получить список методов которые этому примеру удовлетворяют. Например на запрос ' trim my spaces '. 'trim my spaces' он ответит #withBlanksTrimmed.
Ладно, хватит истории, вот пара проектов которые прямо сейчас в разработке. Первый, собравший 300k$ на кикстартере, Light table. Это IDE которая должна решить несколько известных проблем и серьёзно пересмотреть постановку других. На данный момент она нацелена на поддержку Clojure, JavaScript и Python. Среди наиболее интересных фичей:
с блэкджеком и монадами.
Проект Lamdu параллельно развивает язык и среду разработки для него. Язык построен на основе Haskell с изменениями направленными на большую явность конструкций и адаптацию для визуального представления структуры программ. В частности он вводит обязательные имена параметров для функций и явно отображает типы в лямбда-выражениях.
Среда построена вокруг структурированного представления и редактирования кода с выделением различных элементов шрифтом и цветом. Редактор позволяет вводить код только в предназначенные для этого слоты, контролирует после ввода тип, предполагаются умные автодополнения. Ошибки типизации получаются хорошо локализованы (не могу не заметить что оригинальный хаскель тут от C++ ушёл совсем не далеко, точность ошибки компенсируется неопределённостью её местоположения).
Редактор автоматически применяет визуальную свёртку сложных языковых конструкций (вроде лямбд и аннотаций параметров как на картинке). Среди целей проекта предоставление билиотекам возможности настраивать отображение для своих структур данных и функций.
Также предполагается поддержка "regression debugging", то есть автоматический контроль хода выполнения теста и поиск изменения которое сломало тест. Примерно можно сказать что IDE будет запоминать все промежуточные результаты вычисления функций и сравнивать их для каждой версии. Хотя в контексте ленивости языка всё явно будет хитрее.
Помимо попыток привнести глобальные улучшения в IDE есть несколько инструментов реализующие какие-то конкретные компоненты гораздо лучше, чем принято в мэйнстриме.
Вот например Projucer, инструмент визуальный разработки для Juce. Ничего сверхъестественного, просто быстрая связь кода и картинки, но всё-таки поживее многочисленных SWING-дизайнеров, а пишется одним человеком.
Ещё один проект, коммерческий, Chronon. Включает в себя "Time Traveling Debugger". Это забавный инструмент который работает с полной записью исполнения программы. Записи получаются с помощью чёрной магии и второго компонента Chronon'а - "Recording Server". Нам же тут интересен дебаггер.
Его центральной фичей является возможность идти не только вперёд но и назад. Найдя ошибку можно вместо того чтобы расставлять бряки в подозрительных местах и перезапускать программу просто пойти назад и посмотреть "откуда пошло". Есть множество приятностей, вроде подсветки активных путей исполнения, истории состояний для переменных и вызовов методов. Много различных фильтров для поиска нужных значений в истории. Для примера можно посмотреть вот это видео.
Последний пример с совсем неожиданного направления, из мира железячных корпораций и кровавого геймдева. PhysX debugger - инструмент для физического движка от nVidia. Он создан для отладки поведения физической модели в сложных сценах. И демонстрирует очень высокий уровень использования различных визуализаций. Если вам интересно визуальное программирование я рекомендую потратить 15 мин и посмотреть обзорный ролик целиком: PVD tutorial video.
В этом инструменте есть множество примеров визуализации разных аспектов динамических сцен и процесса их вычисления, вот интересный кусочек для ленивых (смотреть минуты три). Есть функции бэктрекинга, когда есть бажный объект, есть момент времени когда с ним что-то не так и надо найти откуда проблема появилась. Предусмотрены инструменты для командной работы, можно аннотировать дебаг-сессию оставляя каменты к объектам в определённые моменты времени. Сессия экспортируется и может быть передана коллеге для дальнейшего разбора проблемы. Действительно развитый инструмент для решения весьма нетривиальных задач.
Напоследок уже в который раз не могу не дать ссылку на сайт Bret Victor'а, которого авторы большинства описанных выше проектов называют в числе основных источников идей: worrydream.com. Если Вы ещё не изучили все его материалы стоит немедленно начать.
Пришло время перейти к конструктиву и немного посмотреть как их можно делать среды которые что-то меняют в процессе разработки. Есть изрядное количество инструментов которые пытаются продвинуться за пределы 95го года и существенно изменить способ взаимодействия программиста и разрабатываемой им программы.
Вот например Squeake, первый релиз 96 год. (Небось думали я с потолка год в прошлом посте взял? Правильно думали на самом деле, да и большинство обсуждаемых далее фич были в Smalltalk-80). Я наверно повторяю очень известную вещь, но всё таки: основной чертой Smalltalk является существование live image. В нём нет статического кода и времени исполнения, всё что делает программист порождает объекты в одном образе, этот образ несёт в себе и код и живые объекты на одинаковых правах. Образ содержит и ядро языка, и разрабатываемое приложение, и среду разработки одновременно, при поставке пользователю ненужные классы удаляются.
Подход этот конечно спорный, но он имеет несколько явно положительных результатов. Например при разработке графики можно весьма просто с ней управляться. Например имея нарисованную как-то картинку можно немного её покрутить, примерить к другой картинке, продублировать. Как-то так или так (вторая ссылка покороче, но требует скачивания видяшки). А после соответствующего благоустройства среда позволяет что-то программировать даже детям. Ну и вообще она полна простых приятностей, вот например Object inspector.
"Чего же тут такого интересного?" - многие спросят. Вроде и ничего, но вот 2014 год на дворе а все популярные среды разработки по-прежнему считают что цвет нормально показывать как десятизначное число.
Второй интересной наработкой Smalltalk является method finder. Первая его фича - поиск по ключевым словам более менее освоена современными IDE. А вот вторая, поиск по примеру и сейчас, спустя примерно 30 лет после создания насколько я знаю ни кем не повторена. Если кратко она позволяет ввести пример результата метода при заданных аргументах и получить список методов которые этому примеру удовлетворяют. Например на запрос ' trim my spaces '. 'trim my spaces' он ответит #withBlanksTrimmed.
Ладно, хватит истории, вот пара проектов которые прямо сейчас в разработке. Первый, собравший 300k$ на кикстартере, Light table. Это IDE которая должна решить несколько известных проблем и серьёзно пересмотреть постановку других. На данный момент она нацелена на поддержку Clojure, JavaScript и Python. Среди наиболее интересных фичей:
- Абстрагирование от файлов проекта, редактор показывает и даёт редактировать функции динамически компонуя их по мере просмотра и редактирования исходников.
- Автоматический показ документации и/или исходников связанных функций.
- Интерактивная отладка, как через горячую подмену кода так и через 'watches' которые позволяют записывать и отображать состояние программы в отдельных точках.
Проект Lamdu параллельно развивает язык и среду разработки для него. Язык построен на основе Haskell с изменениями направленными на большую явность конструкций и адаптацию для визуального представления структуры программ. В частности он вводит обязательные имена параметров для функций и явно отображает типы в лямбда-выражениях.
Среда построена вокруг структурированного представления и редактирования кода с выделением различных элементов шрифтом и цветом. Редактор позволяет вводить код только в предназначенные для этого слоты, контролирует после ввода тип, предполагаются умные автодополнения. Ошибки типизации получаются хорошо локализованы (не могу не заметить что оригинальный хаскель тут от C++ ушёл совсем не далеко, точность ошибки компенсируется неопределённостью её местоположения).
Редактор автоматически применяет визуальную свёртку сложных языковых конструкций (вроде лямбд и аннотаций параметров как на картинке). Среди целей проекта предоставление билиотекам возможности настраивать отображение для своих структур данных и функций.
Также предполагается поддержка "regression debugging", то есть автоматический контроль хода выполнения теста и поиск изменения которое сломало тест. Примерно можно сказать что IDE будет запоминать все промежуточные результаты вычисления функций и сравнивать их для каждой версии. Хотя в контексте ленивости языка всё явно будет хитрее.
Помимо попыток привнести глобальные улучшения в IDE есть несколько инструментов реализующие какие-то конкретные компоненты гораздо лучше, чем принято в мэйнстриме.
Вот например Projucer, инструмент визуальный разработки для Juce. Ничего сверхъестественного, просто быстрая связь кода и картинки, но всё-таки поживее многочисленных SWING-дизайнеров, а пишется одним человеком.
Ещё один проект, коммерческий, Chronon. Включает в себя "Time Traveling Debugger". Это забавный инструмент который работает с полной записью исполнения программы. Записи получаются с помощью чёрной магии и второго компонента Chronon'а - "Recording Server". Нам же тут интересен дебаггер.
Его центральной фичей является возможность идти не только вперёд но и назад. Найдя ошибку можно вместо того чтобы расставлять бряки в подозрительных местах и перезапускать программу просто пойти назад и посмотреть "откуда пошло". Есть множество приятностей, вроде подсветки активных путей исполнения, истории состояний для переменных и вызовов методов. Много различных фильтров для поиска нужных значений в истории. Для примера можно посмотреть вот это видео.
Последний пример с совсем неожиданного направления, из мира железячных корпораций и кровавого геймдева. PhysX debugger - инструмент для физического движка от nVidia. Он создан для отладки поведения физической модели в сложных сценах. И демонстрирует очень высокий уровень использования различных визуализаций. Если вам интересно визуальное программирование я рекомендую потратить 15 мин и посмотреть обзорный ролик целиком: PVD tutorial video.
В этом инструменте есть множество примеров визуализации разных аспектов динамических сцен и процесса их вычисления, вот интересный кусочек для ленивых (смотреть минуты три). Есть функции бэктрекинга, когда есть бажный объект, есть момент времени когда с ним что-то не так и надо найти откуда проблема появилась. Предусмотрены инструменты для командной работы, можно аннотировать дебаг-сессию оставляя каменты к объектам в определённые моменты времени. Сессия экспортируется и может быть передана коллеге для дальнейшего разбора проблемы. Действительно развитый инструмент для решения весьма нетривиальных задач.
Напоследок уже в который раз не могу не дать ссылку на сайт Bret Victor'а, которого авторы большинства описанных выше проектов называют в числе основных источников идей: worrydream.com. Если Вы ещё не изучили все его материалы стоит немедленно начать.
вторник, 8 апреля 2014 г.
Альтернативная среда для Racket
Это последний отчёт о моих опытах с Racket и посвящён он сугубо техническому вопросу, а именно среде разработки. По умолчанию новоиспечённому Racket-разработчику предлагается минимальная среда разработки. Она умеет открывать файлы с исходниками, подсвечивать синтаксис, загружать их в простой (ну очень простой) REPL и запускать под отладкой.
В принципе это не так и мало, но хотелось большего. А именно:
Однако не сразу всё идеально заработало. Файлы с константами было невозможно перезагружать ,rr - Racket выдавал ошибку переопределения имён. Небольшое исследование вопроса показало что это связано с включенными по умолчанию оптимизациями и легко может быть исправлено одной командой, алиас принял вид "xracket="racket -il xrepl --eval '(compile-enforce-module-constants #f)'"".
Остальные 3 функции поставляет Sublime оставалось только подружить его немного с ЛИСПом, благо я не первый кто этим озадачился. Установка пары плагинов налаживает между крепкую дружбу между Racket и Sublime. Для упрощения жизни я использовал Package Control.
Первый и самый насущный - конечно отступы :) lispindent - отлично справляется с этой задачей и даже не пугается квадратных скобок.
Второй - подсветка синтаксиса. В принципе она уже была, однако некоторые конструкции понимала не корректно. В частности совершенно убивали подсветку литералы #something а также функции и структуры не появлялись в списке символов. За пару дней поправил всё это и даже заслал патч в основную ветку проекта.
Ну и последняя доводка из области эстетства - красивая подсветка скобок и быстрый переход между ними. Делается плагином Bracket Highlighter. Очень удобно показывает границы блока слева, рядом с номерами строк а также позволяет включить "усиленную подсветку" - дополнительно подсветить весь текст принадлежащий выделенному блоку. Плюшки вроде удалить блок или перейти во внешний также присутствуют.
Для того чтобы всё это дружило потребовалось буквально пара настроек.
Во-первых надо включить lispindent для соответствующего языка следующей настройкой в lispindent.sublime-settings (если используется Package Control то его можно открыть через меню Preferences->Package settings).
В принципе это всё. Я первый раз что-то глубоко копал в настройку Sublime и результат мне очень понравился. Всё, вплоть до написания своего плагина, решается достаточно легко и работает вполне безглючно.
В принципе это не так и мало, но хотелось большего. А именно:
- Полноценного REPL который бы помнил историю команд и умел подгружать изменения без полного перезапуска.
- Быстрой навигации по именам функций и структур.
- Богатого функционалом текстового редактора, в частности мультивыделения и быстрого поиска.
- Поддержки проектов, по крайней мере коллекций файлов по которым можно искать.
Однако не сразу всё идеально заработало. Файлы с константами было невозможно перезагружать ,rr - Racket выдавал ошибку переопределения имён. Небольшое исследование вопроса показало что это связано с включенными по умолчанию оптимизациями и легко может быть исправлено одной командой, алиас принял вид "xracket="racket -il xrepl --eval '(compile-enforce-module-constants #f)'"".
Остальные 3 функции поставляет Sublime оставалось только подружить его немного с ЛИСПом, благо я не первый кто этим озадачился. Установка пары плагинов налаживает между крепкую дружбу между Racket и Sublime. Для упрощения жизни я использовал Package Control.
Первый и самый насущный - конечно отступы :) lispindent - отлично справляется с этой задачей и даже не пугается квадратных скобок.
Второй - подсветка синтаксиса. В принципе она уже была, однако некоторые конструкции понимала не корректно. В частности совершенно убивали подсветку литералы #something а также функции и структуры не появлялись в списке символов. За пару дней поправил всё это и даже заслал патч в основную ветку проекта.
Ну и последняя доводка из области эстетства - красивая подсветка скобок и быстрый переход между ними. Делается плагином Bracket Highlighter. Очень удобно показывает границы блока слева, рядом с номерами строк а также позволяет включить "усиленную подсветку" - дополнительно подсветить весь текст принадлежащий выделенному блоку. Плюшки вроде удалить блок или перейти во внешний также присутствуют.
Для того чтобы всё это дружило потребовалось буквально пара настроек.
Во-первых надо включить lispindent для соответствующего языка следующей настройкой в lispindent.sublime-settings (если используется Package Control то его можно открыть через меню Preferences->Package settings).
{
"languages": {
"racket": {
"syntax": "Racket.tmLanguage"
}
}
}
В настройках Bracket Highlighter, которые bh_core.sublime-settings (также легко открываются через меню Preferences->Package settings) надо только найти и настроить под себя параметр high_visibility_style, мне понравился underline. Также удобно повесить переключение этого режима на хоткей, для этого в Default.sublime-keymap надо дописать{
"keys": ["ctrl+\\"],
"command": "bh_toggle_high_visibility"
}
Ну и проверить что файлы .rkt открываются как Racket по умолчанию.В принципе это всё. Я первый раз что-то глубоко копал в настройку Sublime и результат мне очень понравился. Всё, вплоть до написания своего плагина, решается достаточно легко и работает вполне безглючно.
среда, 26 марта 2014 г.
IDE как зло
Уже очень давно начиная новый проект с запуска IDE и создания проекта в ней я чувствую смутное беспокойство. Лёгкий страх, что я принимаю решение которое не смогу пересмотреть, что проект получает пробную дозу и не сможет жить без новых.
Этот пост скорее дамп мыслей чем законченная идея. Однако мысли эти посещают меня довольно давно и мне кажется неплохой идей их зафиксировать. Мыслей этих примерно 4:
Давайте посмотрим по внимательнее например на навигацию. Думаете выжила бы джава сама по себе со своей безумной связью путей к исходникам и имён пакетов? Да ещё с необходимостью дублировать эту информацию в начале файла? Отсутствием относительных импортов? Только благодаря массовому использованию IDE этот ужас был хоть как-то жизнеспособен и во многом из за него на джаве трудно писать не имея громоздкой оболочки вокруг редактора, которая генерирует и сама же прячет бессмысленные строки.
Рефакторинг? Да то же самое, весь мой рефакторинг в питоне укладывается в grep и Ctrl+R. Просто нет дублирования информации о модулях и сама питонья культура не приветствует создание пакетов "просто так".
Отладка не менее переоценена. Да иногда полезно остановить программу и посмотреть что там как внутри. Но почти всегда ключ лежит в истории. И важно посмотреть историю создания или изменения какой-то структуры, а не текущий снимок и по вспоминать что ты видел 50 шагов назад. И помогает тут не остановка и прокликивание деревьев из объектов и ссылок а возможность легко оставить запись в логе из любого места программы и продуманное, настраиваемое представление объектов в этом логе.
Реальная польза от IDE - перетаскивание неудачных решений в дизайне языков и библиотек из области "фатальный бред" в область "можно терпеть". Не более. Бывает ли такое решение проблем необходимо? Конечно бывает. Но надо ли им пользоваться по умолчанию?
Печальна также и ситуация с оценкой проблем создаваемых IDE. Их наблюдается немало, но вспоминать про них - табу. Про то как джавовские null'ы несут зло и хаос написаны многие сотни страниц текстов, про то что IDEA последние 6 лет стабильно фризит на более-менее больших проектах почти никто не вспоминает.Или вернёмся опять к рефакторингу. Несмотря на то что идея изменения структуры кода без изменения его семантики в общем то здравая, тут тоже важна мера. Один из негативных эффектов наличия IDE в наборе инструментов - это откровенный спам рефакторингами. Не нравится имя? Две кнопки, вбить новое и готово! Захотелось перепилить структуру пакетов? Берём мышку, драг, дроп, кровь-кишки-мозги по стенам.
Это конечно хорошо точно подбирать имена под семантику если ты один, но если над проектом работает человек 5 надо немного аккуратнее делать такие вещи. К старому имени уже могло привыкнуть какое-то количество людей, отлаживая очередную ошибку они могут захотеть его найти и потерять много времени. Умные инструменты рефакторинга делают крупные изменения кода неоправданно лёгкими, опасно лёгкими.
Самая очевидная проблема - IDE скрывают реальное положение вещей в коде. Бессистемное именование, иерархии классов в 8 уровней, пинг-понг из вызовов перегрузок и super - всё это успешно маскируется навороченным Hierachy view. Проблема в том что инструменты могут что-то сделать с механической частью проблемы - найти все перегрузки, вызовы, ветвления. Но они бессильны с интеллектуальной частью - программисту всё так же надо разбираться в таких путаных структурах.
В коммерческой разработке на Java и C# сейчас наблюдается крайне болезненный культ IDE. Примерно треть половина что я видел не собиралась иначе чем экспортом из конкретной среды, примерно треть могла нормально разрабатываться в одной единственной среде из-за code style или кодогенерации или того что единственное описание зависимости между модулями - проект в проприетарном формате созданный 5 лет назад.
Самое печальное что при выборе между нормальным инструментом не поддерживаемым средой и костыльной конструкции с типа интеграцией - приоритет при остаётся за последней. Мой любимый пример - Git. Основная особенность этой VCS в том что она не устанавливает никаких связей между ветками и репозиториями. Ветки вашего личного репозитория могут быть связаны с произвольными ветками одного или нескольких удалённых. Эти связи должны определяться гайдлайнами в конкретном проекте, автоматизироваться созданием соответствующих скриптов и алиасов и, в особых случаях, контролироваться хуками. Git - принципиально требует настройки и расширения, без этих шагов он не очень удобен и прямо скажем создаёт проблемы.
Проблема в том что разработчики Git плагинов для IDEшек с которыми я работал либо просто не в курсе этой особенности, либо менеджеры соответствующей квалификации пояснили им что разноцветный дифф важнее для хомяков чем какая-то там расширяемость. Как следствие нельзя настраивать и даже использовать уже настроенные алиасы и нельзя удобно призывать на выполнение внешние скрипты. Зато есть кнопки Update и Commit&Push... Ну можете представить как выглядит история когда проект резко переезжает с SVN, никто не пользуется гуглом а среда представляет такой easy по самое не могу функционал.
Ну и наконец о развитии - для сред разработки оно остановилось где-то в 90. IDEA 12 отличается от от JBuilder 2003 практически только разнообразными окошками навигаторов, а он в свою очередь от Turbo Pasсal 6 в основном наличием индекса символов и файлов. И все вместе они на фоне Squeak, скажем так, не выглядят достижениями трёх пятилеток.
Не хватает реальной интеграции компонентов. Например, как дебагер задействует редактор? Красная полоска в месте останова и попап со значением переменной. И то если программер позаботился о правильном toString, а если нет - пожалуйста прокликивайте дерево объектов. Это в две тысячи четырнадцатом году предел юзабилити и визуализации типа? Я LabView году так в 04-05 поюзабельнее дебагер рисовал.
И кстати о рисовании. В 2014 опять же году, когда всё прогрессивное человечество хреначит колбэки на жаваскрипте встраивает DSL в любой инструмент сложнее cat, для расширения любой IDE мне надо создать модуль(!) плагина, скомпилировать(!) его, положить в нужную папочку а то и прокликать пару форм и перезапустить(!) софтину. Future of programming, чо. Самый доступный способ расширения что предлагается - разлапистая форма настройки брекпоинта с condition и log message (в студии то небось до сих пор и того нет?).
Почему кроме строк в редакторе не подсветить методы и классы в навигаторах и аутлайне файла? Почему бы кроме плоского текстового лога не добавить к дебагеру какой-нибудь таймлайн и дерево вызовов где можно отставлять пометки? Почему бы не составлять небольшую карту задействованных виртуальных вызовов и их реализаций? Наконец почему не позволить программистам расширять и настраивать это всё по мере необходимости, скриптами и небольшими вставками кода?
Потому что так не делали в 1995 году? Я не вижу других причин.
Этот пост скорее дамп мыслей чем законченная идея. Однако мысли эти посещают меня довольно давно и мне кажется неплохой идей их зафиксировать. Мыслей этих примерно 4:
- Общая польза от IDE крайне переоценена
- Вред от них практически никогда осознанно не рассматривается
- Во многих [коммерческих] проектах есть крайне не здоровая зависимость от них
- Производящие их компании и сообщества уже много лет ничего реально не улучшали
Давайте посмотрим по внимательнее например на навигацию. Думаете выжила бы джава сама по себе со своей безумной связью путей к исходникам и имён пакетов? Да ещё с необходимостью дублировать эту информацию в начале файла? Отсутствием относительных импортов? Только благодаря массовому использованию IDE этот ужас был хоть как-то жизнеспособен и во многом из за него на джаве трудно писать не имея громоздкой оболочки вокруг редактора, которая генерирует и сама же прячет бессмысленные строки.
Рефакторинг? Да то же самое, весь мой рефакторинг в питоне укладывается в grep и Ctrl+R. Просто нет дублирования информации о модулях и сама питонья культура не приветствует создание пакетов "просто так".
Отладка не менее переоценена. Да иногда полезно остановить программу и посмотреть что там как внутри. Но почти всегда ключ лежит в истории. И важно посмотреть историю создания или изменения какой-то структуры, а не текущий снимок и по вспоминать что ты видел 50 шагов назад. И помогает тут не остановка и прокликивание деревьев из объектов и ссылок а возможность легко оставить запись в логе из любого места программы и продуманное, настраиваемое представление объектов в этом логе.
Реальная польза от IDE - перетаскивание неудачных решений в дизайне языков и библиотек из области "фатальный бред" в область "можно терпеть". Не более. Бывает ли такое решение проблем необходимо? Конечно бывает. Но надо ли им пользоваться по умолчанию?
Печальна также и ситуация с оценкой проблем создаваемых IDE. Их наблюдается немало, но вспоминать про них - табу. Про то как джавовские null'ы несут зло и хаос написаны многие сотни страниц текстов, про то что IDEA последние 6 лет стабильно фризит на более-менее больших проектах почти никто не вспоминает.Или вернёмся опять к рефакторингу. Несмотря на то что идея изменения структуры кода без изменения его семантики в общем то здравая, тут тоже важна мера. Один из негативных эффектов наличия IDE в наборе инструментов - это откровенный спам рефакторингами. Не нравится имя? Две кнопки, вбить новое и готово! Захотелось перепилить структуру пакетов? Берём мышку, драг, дроп, кровь-кишки-мозги по стенам.
Это конечно хорошо точно подбирать имена под семантику если ты один, но если над проектом работает человек 5 надо немного аккуратнее делать такие вещи. К старому имени уже могло привыкнуть какое-то количество людей, отлаживая очередную ошибку они могут захотеть его найти и потерять много времени. Умные инструменты рефакторинга делают крупные изменения кода неоправданно лёгкими, опасно лёгкими.
Самая очевидная проблема - IDE скрывают реальное положение вещей в коде. Бессистемное именование, иерархии классов в 8 уровней, пинг-понг из вызовов перегрузок и super - всё это успешно маскируется навороченным Hierachy view. Проблема в том что инструменты могут что-то сделать с механической частью проблемы - найти все перегрузки, вызовы, ветвления. Но они бессильны с интеллектуальной частью - программисту всё так же надо разбираться в таких путаных структурах.
В коммерческой разработке на Java и C# сейчас наблюдается крайне болезненный культ IDE. Примерно треть половина что я видел не собиралась иначе чем экспортом из конкретной среды, примерно треть могла нормально разрабатываться в одной единственной среде из-за code style или кодогенерации или того что единственное описание зависимости между модулями - проект в проприетарном формате созданный 5 лет назад.
Самое печальное что при выборе между нормальным инструментом не поддерживаемым средой и костыльной конструкции с типа интеграцией - приоритет при остаётся за последней. Мой любимый пример - Git. Основная особенность этой VCS в том что она не устанавливает никаких связей между ветками и репозиториями. Ветки вашего личного репозитория могут быть связаны с произвольными ветками одного или нескольких удалённых. Эти связи должны определяться гайдлайнами в конкретном проекте, автоматизироваться созданием соответствующих скриптов и алиасов и, в особых случаях, контролироваться хуками. Git - принципиально требует настройки и расширения, без этих шагов он не очень удобен и прямо скажем создаёт проблемы.
Проблема в том что разработчики Git плагинов для IDEшек с которыми я работал либо просто не в курсе этой особенности, либо менеджеры соответствующей квалификации пояснили им что разноцветный дифф важнее для хомяков чем какая-то там расширяемость. Как следствие нельзя настраивать и даже использовать уже настроенные алиасы и нельзя удобно призывать на выполнение внешние скрипты. Зато есть кнопки Update и Commit&Push... Ну можете представить как выглядит история когда проект резко переезжает с SVN, никто не пользуется гуглом а среда представляет такой easy по самое не могу функционал.
Ну и наконец о развитии - для сред разработки оно остановилось где-то в 90. IDEA 12 отличается от от JBuilder 2003 практически только разнообразными окошками навигаторов, а он в свою очередь от Turbo Pasсal 6 в основном наличием индекса символов и файлов. И все вместе они на фоне Squeak, скажем так, не выглядят достижениями трёх пятилеток.
Не хватает реальной интеграции компонентов. Например, как дебагер задействует редактор? Красная полоска в месте останова и попап со значением переменной. И то если программер позаботился о правильном toString, а если нет - пожалуйста прокликивайте дерево объектов. Это в две тысячи четырнадцатом году предел юзабилити и визуализации типа? Я LabView году так в 04-05 поюзабельнее дебагер рисовал.
И кстати о рисовании. В 2014 опять же году, когда всё прогрессивное человечество хреначит колбэки на жаваскрипте встраивает DSL в любой инструмент сложнее cat, для расширения любой IDE мне надо создать модуль(!) плагина, скомпилировать(!) его, положить в нужную папочку а то и прокликать пару форм и перезапустить(!) софтину. Future of programming, чо. Самый доступный способ расширения что предлагается - разлапистая форма настройки брекпоинта с condition и log message (в студии то небось до сих пор и того нет?).
Почему кроме строк в редакторе не подсветить методы и классы в навигаторах и аутлайне файла? Почему бы кроме плоского текстового лога не добавить к дебагеру какой-нибудь таймлайн и дерево вызовов где можно отставлять пометки? Почему бы не составлять небольшую карту задействованных виртуальных вызовов и их реализаций? Наконец почему не позволить программистам расширять и настраивать это всё по мере необходимости, скриптами и небольшими вставками кода?
Потому что так не делали в 1995 году? Я не вижу других причин.
понедельник, 17 марта 2014 г.
Land of Lisp
Дочитал сабж. Как можно догадаться книга посвящена языку LISP и выстроена очень своеобразным образом. Основная идея: обучить языку через написание игр. Этот, на первый взгляд направленный на детей, подход в моём случае имел изрядный успех.
Писать игры по ходу книги отличная идея по нескольким причинам. Во-первых это заставляет автора начинать с практически важных, базовых элементов языка. С того с чем в будущем придётся сталкиваться чаще всего, а не с того что педагогически "правильнее". Во-вторых иллюстрации получаются заметно увлекательнее. Ну и наконец игры по своей природе открыты для хакинга, придумать дополнение к числам Фибоначчи гораздо сложнее чем к игре "угадай число".
Скажу честно не все примеры я аккуратно реализовывал и запускал, но примерно 60% задач из книги я выполнил. Что-то расширил, где-то поменял структуру кода - подход с созданием игр в книге даёт читателю очень высокий уровень вовлечённости и это прекрасно.
По содержанию... Книжка раскрывает все основные вопросы программирования: встроенные и пользовательские типы данных, основные конструкции языка, способы их расширения, ввод-вывод. Последовательность практических примеров как мне кажется совершенно логична, хотя порядок в котором вводятся языковые средства немного не отточен.
Код примеров на мой вкус немного грязноват. Много где можно радикально улучшить читабельность простыми изменениями вроде добавления структур вместо списков и магических ca*r'ов, заменой анонимных функций на именованные. Стиль который она оставляет после себя как мне кажется нельзя назвать изящным, но это мнение нуба явно тяготеющего к диалектам Scheme-семейства а не Common для которого написана книга.
И да, просто копировать примеры в таком виде было не очень интересно и решение нашлось само собой - я недавно начал ковырять Racket а книжка то написана под Common LISP! Вот и совместил приятное с полезным, транслировал примеры на современный Racket. И я теперь практически лисп-переводчик с опытом работы :) Кстати благодаря этой книжке я сделал и небольшой (и кажется первый сколько-нибудь полезный) вклад в опенсурс: изрядно подвыправил Racket плагин для Sublime.
В общем несмотря на то что книжка вроде не блестящая при должном настрое и желании что-то поделать руками она приночит массу удовольствия. Рекомендую!
Писать игры по ходу книги отличная идея по нескольким причинам. Во-первых это заставляет автора начинать с практически важных, базовых элементов языка. С того с чем в будущем придётся сталкиваться чаще всего, а не с того что педагогически "правильнее". Во-вторых иллюстрации получаются заметно увлекательнее. Ну и наконец игры по своей природе открыты для хакинга, придумать дополнение к числам Фибоначчи гораздо сложнее чем к игре "угадай число".
Скажу честно не все примеры я аккуратно реализовывал и запускал, но примерно 60% задач из книги я выполнил. Что-то расширил, где-то поменял структуру кода - подход с созданием игр в книге даёт читателю очень высокий уровень вовлечённости и это прекрасно.
По содержанию... Книжка раскрывает все основные вопросы программирования: встроенные и пользовательские типы данных, основные конструкции языка, способы их расширения, ввод-вывод. Последовательность практических примеров как мне кажется совершенно логична, хотя порядок в котором вводятся языковые средства немного не отточен.
Код примеров на мой вкус немного грязноват. Много где можно радикально улучшить читабельность простыми изменениями вроде добавления структур вместо списков и магических ca*r'ов, заменой анонимных функций на именованные. Стиль который она оставляет после себя как мне кажется нельзя назвать изящным, но это мнение нуба явно тяготеющего к диалектам Scheme-семейства а не Common для которого написана книга.
И да, просто копировать примеры в таком виде было не очень интересно и решение нашлось само собой - я недавно начал ковырять Racket а книжка то написана под Common LISP! Вот и совместил приятное с полезным, транслировал примеры на современный Racket. И я теперь практически лисп-переводчик с опытом работы :) Кстати благодаря этой книжке я сделал и небольшой (и кажется первый сколько-нибудь полезный) вклад в опенсурс: изрядно подвыправил Racket плагин для Sublime.
В общем несмотря на то что книжка вроде не блестящая при должном настрое и желании что-то поделать руками она приночит массу удовольствия. Рекомендую!
четверг, 30 января 2014 г.
Линкопост #2
Пачка ссылок за последние... 4 месяца. Для начала связанное с Racket.
The Racket Way - доклад на strange loop, рассказ о дизайне или даже скорее философии Racket. Несколько наглядных примеров потребности в мета-языках. Достаточно подробно показана возможность плавной эволюции по пути приложение, библиотека, internal DSL, подмножество языка и новый язык со специальным синтаксисом в конце. Полезно посмотреть и как введение в Racket и чтобы понять что можно делать с мета-языками. Мне такая схема показалось существенно более жизнеспособной чем применение MPS или создание external DSL с 0.
Fear of Macros - что-то вроде мини-книги или большого цикла статей о макросах в Racket. Вопрос раскрывается гораздо удачнее чем в основной документации. Автор начинает с простого но низкоуровнего примера и оттуда движется к высокоуровневым pattern-based macros. Оставляет целостное понимание что такое syntax object, expansion и т.д. Интересно будет тем что интересуется мета-программированием или изучает схему.
Frog - аналог Octopress на Racket, вроде попроще в инсталляции. Выглядит симпатично, использует модный Bootstrap. В качестве языка разметки в том числе можно использовать Scribble или Markdown. Код более менее читаемый, в общем неплохой проект для поиграться с языком и посмотреть как на нём пишут реальный софт.
Немного холиваров и вбросов.
We're Doing It All Wrong - доклад от бывшего участника разработки компилятора Scala. Подробно, хотя и крайне эмоционально рассматривает различные проблемы в самом языке и текущей реализации компилятора. Немного сумбурно - неявно мешаются в кучу проблемы дизайна языка, библиотеки, кода компилятора а в конце вообще выдвигается идея некоего идеального языка не совсем очевидно связанная с озвученными проблемами. Но в любом случае стоит уделить внимание если вы планируете куда-то внедрять скалу.
The Unreasonable Effectiveness of C - наверно одна из самых интересных статей из прочитанных мной за последнее время. Анализируется почему старый, низкоуровневый язык C был и остаётся популярным и весьма успешным. Строго рекомендовано к прочтению фанбоям хаскелей, скал, джаваскриптов, коков и всего остального что конвертирует силу мысли в быстрые и безбажные программы.
Честно про современные веб-технологии - запихнул в этот раздел, но вообще считаю все написанное в посте чистейшей правдой. Не могу не добавить что надвигающийся HTTP 2.0 обещает сделать реализацию и поддержку веб-сервера уделом пары-тройки корпораций.
Хорошие статьи по практике программирования.
Computational Geometry in Python: From Theory to Implementation - понятный и краткий обзор вычислительной геометрии с точки зрения программиста. Хорошо проиллюстрирован кодом на питоне. Очень приятный способ размять мозги в новой области.
How to design and code a complete program - очень хорошая серия статей о дизайне в функциональном стиле. Построена вокруг одного примера - CRUD приложения для веба. Просто и наглядно излагаются некоторые неочевидные концепции. Код на хаскеле.
Ну и немного философии. Why education is so difficult and contentious - большой обзор происхождения и актуальных проблем современного образования. Интересна тем что внятно формулирует цели образования и показывает что они не очень то совпадают с интересами современного индивида, да и во многом общества.
Последняя ссылка на тему личного управления задачами. Bullet Journal - система ведения TODO/календаря в бумажном блокноте. Показалась удобной, сам тестирую.
The Racket Way - доклад на strange loop, рассказ о дизайне или даже скорее философии Racket. Несколько наглядных примеров потребности в мета-языках. Достаточно подробно показана возможность плавной эволюции по пути приложение, библиотека, internal DSL, подмножество языка и новый язык со специальным синтаксисом в конце. Полезно посмотреть и как введение в Racket и чтобы понять что можно делать с мета-языками. Мне такая схема показалось существенно более жизнеспособной чем применение MPS или создание external DSL с 0.
Fear of Macros - что-то вроде мини-книги или большого цикла статей о макросах в Racket. Вопрос раскрывается гораздо удачнее чем в основной документации. Автор начинает с простого но низкоуровнего примера и оттуда движется к высокоуровневым pattern-based macros. Оставляет целостное понимание что такое syntax object, expansion и т.д. Интересно будет тем что интересуется мета-программированием или изучает схему.
Frog - аналог Octopress на Racket, вроде попроще в инсталляции. Выглядит симпатично, использует модный Bootstrap. В качестве языка разметки в том числе можно использовать Scribble или Markdown. Код более менее читаемый, в общем неплохой проект для поиграться с языком и посмотреть как на нём пишут реальный софт.
Немного холиваров и вбросов.
We're Doing It All Wrong - доклад от бывшего участника разработки компилятора Scala. Подробно, хотя и крайне эмоционально рассматривает различные проблемы в самом языке и текущей реализации компилятора. Немного сумбурно - неявно мешаются в кучу проблемы дизайна языка, библиотеки, кода компилятора а в конце вообще выдвигается идея некоего идеального языка не совсем очевидно связанная с озвученными проблемами. Но в любом случае стоит уделить внимание если вы планируете куда-то внедрять скалу.
The Unreasonable Effectiveness of C - наверно одна из самых интересных статей из прочитанных мной за последнее время. Анализируется почему старый, низкоуровневый язык C был и остаётся популярным и весьма успешным. Строго рекомендовано к прочтению фанбоям хаскелей, скал, джаваскриптов, коков и всего остального что конвертирует силу мысли в быстрые и безбажные программы.
Честно про современные веб-технологии - запихнул в этот раздел, но вообще считаю все написанное в посте чистейшей правдой. Не могу не добавить что надвигающийся HTTP 2.0 обещает сделать реализацию и поддержку веб-сервера уделом пары-тройки корпораций.
Хорошие статьи по практике программирования.
Computational Geometry in Python: From Theory to Implementation - понятный и краткий обзор вычислительной геометрии с точки зрения программиста. Хорошо проиллюстрирован кодом на питоне. Очень приятный способ размять мозги в новой области.
How to design and code a complete program - очень хорошая серия статей о дизайне в функциональном стиле. Построена вокруг одного примера - CRUD приложения для веба. Просто и наглядно излагаются некоторые неочевидные концепции. Код на хаскеле.
Ну и немного философии. Why education is so difficult and contentious - большой обзор происхождения и актуальных проблем современного образования. Интересна тем что внятно формулирует цели образования и показывает что они не очень то совпадают с интересами современного индивида, да и во многом общества.
Последняя ссылка на тему личного управления задачами. Bullet Journal - система ведения TODO/календаря в бумажном блокноте. Показалась удобной, сам тестирую.
вторник, 21 января 2014 г.
"(Не)совершенная случайность"
Прочитал эту замечательную книгу буквально за 3 дня - наверно самое быстрое прочтение в моей жизни.
Книга посвящена теории вероятности и содержит её рассмотрение сразу с нескольких ракурсов и автор не старается как-то их разделять по ходу изложения. Теория рассказывается от простого к сложному с привлечением разных ракурсов по мере необходимости. В принципе в кинге уверенно распознаются три аспекта.
Во-первых рассказываются, именно рассказываются, сами тервер и матстат. Простым человеческим языком, без сигма-алгебры, условной вероятности и вообще без единой формулы. Очень понятно и просто, книга как ни одна сильна в том чтобы построить контекст вокруг объектов из мира статистики, добавить к заученным в универе терминам очень много семантики. По последовательности изложения она фактически совпадает с классическим курсом тервера и примерно первой третью курса статистики.
Второй предмет рассмотрения книги - история развития тервера. Изложения всех законов органично переплетено с рассказом истории их формирования. Много внимания уделено биографии учёных, их отношениям с другими науками. Для меня как для любителя истории науки этот аспект книги был наиболее интересен.
Третий, и кажется центральный для автора, аспект книги - психологический. При изложении очередной концепции рассказывается как люди склонны ошибаться при его применении к жизненным ситуациям. Всегда приводится несколько примеров наивного применения тервера к жизни. В каждом случае подробно рассматривается корректный способ анализа той или иной проблемы и какие ловушки психологии приводят массы (а иногда и уважаемых учёных) к заблуждениям. В частности подробно рассмотрена знаменитая задача Монти Холла, и кажется доступно даже для меня доказано решение.
Также хочется отметить перевод - действительно хороший и грамотный. Я не заметил ни одной шероховатости в терминологии и описании сложных ошибок использования тервера. Единственный момент который вызвал моё непонимание это название книги. На английском она называется "Drunkard's Walk" то есть "Походка пьяного" как я понимаю.
Книга однозначно рекомендуется и любителям истории, тем кто не теряет надежду понять тервер и даже увлекающимся психологией. Книга будет полезна абсолютно всем вовлечённым в процессы принятия решений, особенно тем кто управляет сложными тех. процессами вроде разработки ПО.
Книга однозначно рекомендуется и любителям истории, тем кто не теряет надежду понять тервер и даже увлекающимся психологией. Книга будет полезна абсолютно всем вовлечённым в процессы принятия решений, особенно тем кто управляет сложными тех. процессами вроде разработки ПО.
четверг, 9 января 2014 г.
Clojure vs. Racket - мои наблюдения
Недавно потянуло меня подосвежить в памяти ЛИСП, в качестве учебного пособия и источника игрушечных проектов я без колебаний выбрал LoL. А вот над тем какой диалект выбрать призадумался на пару дней. Вообще сама ситуация довольно примечательна, ЛИСП насколько я понимаю сейчас является единственным языком с несколькими совершенно живыми и здравствующими диалектами. Встраиваемые я сразу отбросил, но и вполне себе самостоятельных осталось порядочно.
В финал вышли два - Clojure и Racket. Common Lisp я отбросил так как в нём многовато на мой вкус специальных форм, макросов и прочих интересных вещей, мне он показался достаточно непоследовательным. Для двух финалистов есть известное и по сути очень хорошее сравнение. Его вывод достаточно однозначен - на практике программировать на Clojure гораздо удобнее, а для учебных целей у диалектов паритет. Однако пост от 2010 года и часть данных там устарели. Ниже я перечислю все устаревшие моменты в том посте ну и приведу пару фактов которые склонили мой выбор в пользу ракеты.
Первый довод автора - отсутствие общих интерфейсов у разных структур данных, в частности списка и вектора. На сколько я могу судить эта проблема решена. Во-первых введено понятие последовательности и определён ряд удобных форм и функкций для работы с ними. Кроме того добавлена наконец поддержка абстрактных типов данных. Кажется полностью аналогична протоколам из Clojure.
Вторая проблема: не консистентная работа со словарями. Она исправлена во-первых ведением протокола для словарей, поддерживаемого из коробки хеш-таблицами и списками пар. Во-вторых богатым семейством функций in-dict* по преобразованию словарей в последовательности.
Последнее что не понравилось автору - отсутствие ленивых списков или потоков. Она тоже исправлена на настоящий момент - есть неплохая на мой взгляд библиотека потоков, хорошо прилегающая в нужных местах к последовательностям и словарям.
В принципе я согласен с тем что сейчас Clojure ближе к промышленности. Но связано это в основном с количество активных фанатов которые активно её рекламируют и, надеюсь, репортят баги.
Почему я в итоге выбрал Racket? Буквально две причины. Во-первых в Clojure слишком много на мой вкус синтаксических конструкций. Да да, ЛИСП с избытком конструкций, куда мир катится? Куча каких-то скобочек, шапочек, двоеточий и стрелочек... Всякие навороты вроде ключевых слов очень активно используются стандартной библиотекой. Вторая - JVM. Сейчас я уж слишком привязан к ней, надо как-то расширять кругозор.
В финал вышли два - Clojure и Racket. Common Lisp я отбросил так как в нём многовато на мой вкус специальных форм, макросов и прочих интересных вещей, мне он показался достаточно непоследовательным. Для двух финалистов есть известное и по сути очень хорошее сравнение. Его вывод достаточно однозначен - на практике программировать на Clojure гораздо удобнее, а для учебных целей у диалектов паритет. Однако пост от 2010 года и часть данных там устарели. Ниже я перечислю все устаревшие моменты в том посте ну и приведу пару фактов которые склонили мой выбор в пользу ракеты.
Первый довод автора - отсутствие общих интерфейсов у разных структур данных, в частности списка и вектора. На сколько я могу судить эта проблема решена. Во-первых введено понятие последовательности и определён ряд удобных форм и функкций для работы с ними. Кроме того добавлена наконец поддержка абстрактных типов данных. Кажется полностью аналогична протоколам из Clojure.
Вторая проблема: не консистентная работа со словарями. Она исправлена во-первых ведением протокола для словарей, поддерживаемого из коробки хеш-таблицами и списками пар. Во-вторых богатым семейством функций in-dict* по преобразованию словарей в последовательности.
Последнее что не понравилось автору - отсутствие ленивых списков или потоков. Она тоже исправлена на настоящий момент - есть неплохая на мой взгляд библиотека потоков, хорошо прилегающая в нужных местах к последовательностям и словарям.
В принципе я согласен с тем что сейчас Clojure ближе к промышленности. Но связано это в основном с количество активных фанатов которые активно её рекламируют и, надеюсь, репортят баги.
Почему я в итоге выбрал Racket? Буквально две причины. Во-первых в Clojure слишком много на мой вкус синтаксических конструкций. Да да, ЛИСП с избытком конструкций, куда мир катится? Куча каких-то скобочек, шапочек, двоеточий и стрелочек... Всякие навороты вроде ключевых слов очень активно используются стандартной библиотекой. Вторая - JVM. Сейчас я уж слишком привязан к ней, надо как-то расширять кругозор.
понедельник, 23 декабря 2013 г.
Чем хорош текст
Недавно отсмотрел серию презентаций небезызвестного Брета Виктора. Кстати всем их рекомендую. Последнюю из них, "The Future Of Programming", я бы назвал вторым лучшим техническим докладом что я видел (Угадайте какой первый?).
Теперь собственно о чём хотел написать. Если не вдаваться в детали, то основная мысль Виктора - графические интерфейсы должны полностью доминировать над текстово-командными. Причём не столько в области весёлых фермеров, сколько в сложных ответственных областях, таких как разработка ПО.
За деталями и доказательствами я предлагаю отправится к самим презентациям. Здесь же я хочу рассмотреть несколько свойств текста которые, как мне кажется, пока не присущи графике. Возможно не в силу каких фундаментальных проблем, но из за малого количества исследований в этих направлениях. Буду рад опровержениям моей точки зрения в виде ссылок на статьи а может работающий софт.
Первое и важнейшее свойство - переносимость. Искажение картинки - может радикально изменить её восприятие а добиться одинакового отображения какой-то графики на разных устройствах - сложная задача. Я не говорю что она не решается, тем более что не всякие искажения фатальны для разных типов информации, но она по крайней мере есть. Для текста практически любое разумное искажение (читай шрифт) не мешает восприятию.
Кусок текста можно послать коллеге в скайпе или вставить в научную статью с минимальными затратами. Текст ничего не потеряет, хитрая визуализация динамически скрывающая часть свойств и связей объектов может оказаться не пригодной для простой пересылки если на другом конце нет ПО которое её переваривает. В конце концов текст можно перенести на бумагу и (если он разумного размера) взять с собой на необитаемый остров.
Вторым важным достоинством текстов является регулярность структуры. Один символ идёт за другим. В отличии от графов, где очень трудно веделить какой-то тип регулярности. Для нашего мозга это не самая большая помеха, но для алгоритмической обработки серьёзная проблема. Простые проблемы вроде поиска по шаблону превращаются очень интересные если их перевести из мира текстов в мир графики. Просто понять что здесь может значить "шаблон" уже сложно.
У этого достоинства есть пара очень важных подпунктов. Первый - это валидация. Для текстов есть развитая теория грамматик и не менее развитая практика написания парсеров. Есть ли они для графов/картинок?
Второй - возможность получения разницы между двумя объектами. Да да, тот самый diff. Он жизненно важен для поддержки софта, без него нельзя говорить о более-менее масштабном применении визуальных языков.
Думаю мненно этими преимуществами текста вызвана примерно нулевая популярность всяческих графических языков и сред разработки для них.
Теперь собственно о чём хотел написать. Если не вдаваться в детали, то основная мысль Виктора - графические интерфейсы должны полностью доминировать над текстово-командными. Причём не столько в области весёлых фермеров, сколько в сложных ответственных областях, таких как разработка ПО.
За деталями и доказательствами я предлагаю отправится к самим презентациям. Здесь же я хочу рассмотреть несколько свойств текста которые, как мне кажется, пока не присущи графике. Возможно не в силу каких фундаментальных проблем, но из за малого количества исследований в этих направлениях. Буду рад опровержениям моей точки зрения в виде ссылок на статьи а может работающий софт.
Первое и важнейшее свойство - переносимость. Искажение картинки - может радикально изменить её восприятие а добиться одинакового отображения какой-то графики на разных устройствах - сложная задача. Я не говорю что она не решается, тем более что не всякие искажения фатальны для разных типов информации, но она по крайней мере есть. Для текста практически любое разумное искажение (читай шрифт) не мешает восприятию.
Кусок текста можно послать коллеге в скайпе или вставить в научную статью с минимальными затратами. Текст ничего не потеряет, хитрая визуализация динамически скрывающая часть свойств и связей объектов может оказаться не пригодной для простой пересылки если на другом конце нет ПО которое её переваривает. В конце концов текст можно перенести на бумагу и (если он разумного размера) взять с собой на необитаемый остров.
Вторым важным достоинством текстов является регулярность структуры. Один символ идёт за другим. В отличии от графов, где очень трудно веделить какой-то тип регулярности. Для нашего мозга это не самая большая помеха, но для алгоритмической обработки серьёзная проблема. Простые проблемы вроде поиска по шаблону превращаются очень интересные если их перевести из мира текстов в мир графики. Просто понять что здесь может значить "шаблон" уже сложно.
У этого достоинства есть пара очень важных подпунктов. Первый - это валидация. Для текстов есть развитая теория грамматик и не менее развитая практика написания парсеров. Есть ли они для графов/картинок?
Второй - возможность получения разницы между двумя объектами. Да да, тот самый diff. Он жизненно важен для поддержки софта, без него нельзя говорить о более-менее масштабном применении визуальных языков.
Думаю мненно этими преимуществами текста вызвана примерно нулевая популярность всяческих графических языков и сред разработки для них.
понедельник, 2 декабря 2013 г.
Упрощение кода с case classes - пример
Давненько я ничего не писал с приверами кода... Вот немного практики в продолжение октябрьского поста про читабельность. Несколько месяцев назад довелось написать вот такой вот класс. Ни функционал ни производительность этого кода особо не важны - затыкалась отдельно взятая дыра в дизайне, кажется успешно :) Если описать этот класс кратко - его экземпляры должны позволять дожидаться получения фиксированного количества результатов. Однако даже беглый осмотр позволяет понять что код тяжеловато читать, для задачи такой сложности, думаю в районе третьей строчки все закрыли гист и вернулись на эту страницу.
Как мне кажется проблемы с читабельностью имеют две основные причины.
Суть выражена в переходе от набора из нескольких аттрибутов к одному но с более сложной структурой.
Было:
Было:
Метод fail тоже неплохо прибавил в выразительности - теперь оба add и fail явно переводят объект из одного состояния в другое. Надо признать что новая версия ведёт себя не точно так-же как старая, новый подход сделал более строгое поведение и более лёгким. add больше не принимает результаты сверх заданного числа, а fail - не изменяет состояние завершённого объекта.
Было:
Было:
Я вижу следующие явные улучшения:
Как мне кажется проблемы с читабельностью имеют две основные причины.
- 3 возможных состояния объекта закодированы в 3 аттрибутах. При этом возможны заведомо бессмысленные состояния (doneCnt != res.size() или ex != null && doneCnt == totCnt).
- Методы fail, add, listen судя по коду предпринимают какие-то действия в слепую, иногда проверяя лишь часть переменных. Как следствие о корректности кода нельзя судить по отдельным фрагментам - нужно думать о поведении всех методов сразу.
Суть выражена в переходе от набора из нескольких аттрибутов к одному но с более сложной структурой.
Было:
private var doneCnt = 0 private var ex: Exception = null private val res = new ArrayList[T](totCnt)Cтало:
private var res: Either[util.ArrayList[T], Exception] = Left(new util.ArrayList[T](totCnt))Помимо экономии пары строк мы добавили немного семантики изменяемому состоянию объектов. Теперь видно что наличие исключения и списка результатов - взаимоисключающие состояния. Но есть ли ещё какие-то скрытые значения в состоянии списка? С одной стороны можно бы это описать в комментариях, но они как известно не проверяются компилятором. Так что я попробовал сделать так чтобы она стала ясна из прочтения первого-же метода.
Было:
def add(item: T) {
lock.lock()
try {
res.add(item)
doneCnt = doneCnt + 1
if (doneCnt == totCnt) {
done.signalAll()
for (lsnr <- succLsnrs)
lsnr(res)
}
}
finally {
lock.unlock()
}
}
Cтало:def add(item: T) {
lock.lock()
try {
res match {
case Left(list) if list.size < totCnt => {
list.add(item)
if (list.size == totCnt) {
done.signalAll()
for (lsnr <- succLsnrs)
lsnr(list)
}
}
case _ => return
}
}
finally {
lock.unlock()
}
}
Смотрим метод add, из него видно что добавления результатов разрешены пока количество элементов в списке меньше totCnt. Здесь уже вырисовывается точная семантика для аттрибута res.- Right(exception) - сбор результатов провалился.
- Left(list) && list.size < totCnt - сбор результатов всё ещё в процессе.
- Left(list) && list.size == totCnt - сбор результатов завершён их все можно получить.
Метод fail тоже неплохо прибавил в выразительности - теперь оба add и fail явно переводят объект из одного состояния в другое. Надо признать что новая версия ведёт себя не точно так-же как старая, новый подход сделал более строгое поведение и более лёгким. add больше не принимает результаты сверх заданного числа, а fail - не изменяет состояние завершённого объекта.
Было:
def fail(e: Exception) {
lock.lock()
try {
ex = e
done.signalAll()
for (lsnr <- failLsnrs)
lsnr(ex)
}
finally {
lock.unlock()
}
}
Cтало:def fail(e: Exception) {
lock.lock()
try {
res match {
case Left(list) if list.size < totCnt => {
res = Right(ex)
done.signalAll()
for (lsnr <- failLsnrs)
lsnr(ex)
}
case _ => return
}
}
finally {
lock.unlock()
}
}
Существенно улучшилась и структура метода get - теперь он сначала дожидается одного из завершённых состояний, потом предпринимает некие действия. Раньше он вынужден был выполнять две по-сути параллельные проверки во время ожидания и по-разному из него выходить.Было:
def get: JavaCollection[T] = {
lock.lock()
try {
while (doneCnt != totCnt) {
if (ex != null)
throw ex
done.await()
}
res
}
finally {
lock.unlock()
}
}
Cтало:def get: JavaCollection[T] = {
lock.lock()
try {
while (res.left.toOption.exists(_.size < totCnt))
done.await()
res match {
case Left(list) => list
case Right(ex) => throw ex
}
}
finally {
lock.unlock()
}
}
Метод listen в принципе изменился так же как get, так что я не буду приводить его код. Вот окончательная версия.Я вижу следующие явные улучшения:
- Вместо 3х скоординированных значений - одно, содержащее результат. Состояний по-прежнему три, но способ их кодирования радикально упростился.
- Методы add, fail, listen изменили форму. Раньше они более а в основном менее явно предполагали некую историю изменений и обновляли состояние на основе этих предположений. Теперь методы инспектируют текущее состояние, и устанавливают новое на его основе. Практически конечный автомат.
- Метод get сначала неявно ожидал нужного состояния одной из двух переменных (ex и size), "произошло исключение или количество результатов сравнялось с ожидаемым". Теперь этот метод явно ожидает пока нарушится одно условие, "существует неполный список результатов". Точек принятия решения в методе изначально было две, стала одна.
Одно общее следствие: класс стало возможно анализировать и читать более мелкими шагами, по одному методу. При этом пространство возможных состояний уменьшилось лишь немного, изменилось лишь их представление и интерпретация.
вторник, 22 октября 2013 г.
Joker
Сходил 15 октября на конференцию Joker. Было хорошо, организация на 5- (минус за грустного чувака в костюме бетменовского джокера, Леджер негодует с того света), подбор докладов на 4. Далее краткий обзор докладов на которых я присутствовал.
"Факты и заблуждения о Java-сериализации" Хороший доклад, я так понимаю ничего что нельзя прочитать в спеке сказано не было... Но ктож её читал. Достаточно компактно рассказано о основных возможностях стандартной джавовской сериализации, и случаях когда не надо искать ей замену. По крайней мере для меня было познавательно.
"Парадигмы ООП, основы здравого дизайна и архитектуры Java приложений" Не очень понравился. Рассказывал какой-то коуч, так что представление было бодрым. Но и рассказ согласно званию автора был про очевидные вещи вроде SOLID и DRY. Много ссылок на "авторитеты" вместо рассмотрения каки-то конкретных решений и их последствий. Много проталкивания одной позиции в давних холиварах вроде interface vs. abstract class или setters/getters vs. public field.
"Компромиссы, или Как проектируются языки программирования" Неплохо. Но в основном содержание свелось к "В Kotlin всё збс.". Я пару раз попробовал набросить на тему что хаки в компиляторе склонные выявляться в самые интересные моменты. Мне сказали что встроили аккуратно но крепко, никто ничего не заметит. Ага...
"Платформа для Видео сроком в квартал" Хороший доклад от техлида Одноклассников. Рассказ был про их хостинг видео. Всё кратко и по делу. Вопросы не отставали от доклада. Очень понравилось. Не так часто разрабы сервисов такого уровня делятся архитектурными решениями и цифрами. Яндекс вон всё больше норовит мап-редьюс фреймворк какой заопенсурсить.
"Факты и заблуждения о Java-сериализации" Хороший доклад, я так понимаю ничего что нельзя прочитать в спеке сказано не было... Но ктож её читал. Достаточно компактно рассказано о основных возможностях стандартной джавовской сериализации, и случаях когда не надо искать ей замену. По крайней мере для меня было познавательно.
"Парадигмы ООП, основы здравого дизайна и архитектуры Java приложений" Не очень понравился. Рассказывал какой-то коуч, так что представление было бодрым. Но и рассказ согласно званию автора был про очевидные вещи вроде SOLID и DRY. Много ссылок на "авторитеты" вместо рассмотрения каки-то конкретных решений и их последствий. Много проталкивания одной позиции в давних холиварах вроде interface vs. abstract class или setters/getters vs. public field.
"Компромиссы, или Как проектируются языки программирования" Неплохо. Но в основном содержание свелось к "В Kotlin всё збс.". Я пару раз попробовал набросить на тему что хаки в компиляторе склонные выявляться в самые интересные моменты. Мне сказали что встроили аккуратно но крепко, никто ничего не заметит. Ага...
"Платформа для Видео сроком в квартал" Хороший доклад от техлида Одноклассников. Рассказ был про их хостинг видео. Всё кратко и по делу. Вопросы не отставали от доклада. Очень понравилось. Не так часто разрабы сервисов такого уровня делятся архитектурными решениями и цифрами. Яндекс вон всё больше норовит мап-редьюс фреймворк какой заопенсурсить.
"The (not so) dark art of Performance Tuning" Откровенная муть. Какие-то эмпирические правила на тему что смотреть при проблемах с производительностью и куда крутить когда что-то увидели. Мало описания смысла циферок в табличках, мало идей на тему как искать проблему когда она не на поверхности. Долгое и непонятное демо в конце.
"Разработка API в Java-проекте: как оказывать влияние на людей и не приобрести врагов" Попытка обобщить хорошие практики в дизайне API на джаве. Много очевидных вещей, но зато автор не ленился приводить альтернативы и трэйд-оффы. Подача мягко говоря хромала, очень монотонная без намёка на акценты речь. Каких-то новых для себя идей не заметил, но как обобщение пойдёт.
"JDK8: Stream style" Хороший технический доклад от ораклового инженера. Довольно кратко и по делу. После него достаточно понятно становится чего ждать от 8го релиза джавы. Были приведены кое-какие интересные детали дизайна. Докладчик опытный, подача на отл. Рекомендуется к просмотру.
"В поисках Tommy Hilfiger" Интересный рассказ про внутренности Lucene. Немного дизайна немного алгоритмов, много задачек ушло в зал, на что-то я даже ответил. Докладчик молодец что (а) выбрал техническую тему, без лирики, (б) с самого начала наладил взаимодействие с аудиторией. Если Кормен на полке покрылся пылью то рекомендуется к просмотру с паузами на вопросах в зал.
UPD: вставил ссылки на записи докладов.
"Разработка API в Java-проекте: как оказывать влияние на людей и не приобрести врагов" Попытка обобщить хорошие практики в дизайне API на джаве. Много очевидных вещей, но зато автор не ленился приводить альтернативы и трэйд-оффы. Подача мягко говоря хромала, очень монотонная без намёка на акценты речь. Каких-то новых для себя идей не заметил, но как обобщение пойдёт.
"JDK8: Stream style" Хороший технический доклад от ораклового инженера. Довольно кратко и по делу. После него достаточно понятно становится чего ждать от 8го релиза джавы. Были приведены кое-какие интересные детали дизайна. Докладчик опытный, подача на отл. Рекомендуется к просмотру.
"В поисках Tommy Hilfiger" Интересный рассказ про внутренности Lucene. Немного дизайна немного алгоритмов, много задачек ушло в зал, на что-то я даже ответил. Докладчик молодец что (а) выбрал техническую тему, без лирики, (б) с самого начала наладил взаимодействие с аудиторией. Если Кормен на полке покрылся пылью то рекомендуется к просмотру с паузами на вопросах в зал.
UPD: вставил ссылки на записи докладов.
среда, 16 октября 2013 г.
ГЭБ
Прочитал сабж. Ещё буду выборочно перечитывать, по крайней мере доказательства...
Что можно сказать?... Автор неимоверно крут. Что бы по науке, технике или искусству вы сейчас не читали - бросайте, читайте эту книгу. Всё-рано после неё захочется перечитать и передумать заново...
От прочтения есть важный бонус. Когда встречается какой-нибудь просветитель рассказывающий что мол нельзя так запросто объяснить зачем там какой-нибудь теркат нужен и что он такое из себя представляет - можно смело говорить что он бездарен и тыкать книжкой в качестве пруфа. Хотя скорее всего будет просто грустно и хотеться уйти...
Ладно хватит абстрактной воды, пора немного рассказать о чём книга. В предисловии Хофштадтер приводит мнения разных людей о том про что эта книга. Так вот, дочитав до конца я понял что очень важного предположения там не хватает. Эта книга - о сложности!
Действительно одна из центральных концепций книги - "странные петли". Системы с обратной связью, но только между всеми уровнями, без начала. Про этакие гармошки свёрнутые в бублик. Про проблемы которые можно начать изучать откуда угодно и и никогда не закончить. Про всю эту жуть которая пугает технаря со стажем, так же как пугала всех математиков ещё 100 лет назад. А может и им до сих пор страшновато?
Сама структура книги образует петлю. Каждая глава начинается с небольшого диалога разыгрывающего очередную концепцию. Она подхватывается текстом который немного расшифровывает её и немного диалоги предыдущих глав. Каждая новая глава привносит больше понимания в предыдущие, открывает незаметные детали.
Книга рассматривает в деталях как можно всё запутать, как сама природа это делает. И вместе с тем показывает как можно что угодно распутать назад, понять, построить устойчивую модель. Например очень кратко и интересно рассказывается про синтез белков и передачу наследственной информации. Не просто о физике процесса, но о семантике разных его компонентов. Я немало времени потратил на биологию в своё время, и уверен что если в профильных книгах такое и можно найти, то в очень немногих.
Книгу стоит прочитать хотя бы ради того чтобы увидеть что даже для бесконечно сложных объектов мы всегда можем найти средства описания, способ думать о них простым и непротиворечивым способом. Найти объяснение очень глубинным свойствам используя очень примитивный базис. Автор не обещает что такие способы мышления будут очень легки, но убеждает в том что они существуют.
Что можно сказать?... Автор неимоверно крут. Что бы по науке, технике или искусству вы сейчас не читали - бросайте, читайте эту книгу. Всё-рано после неё захочется перечитать и передумать заново...
От прочтения есть важный бонус. Когда встречается какой-нибудь просветитель рассказывающий что мол нельзя так запросто объяснить зачем там какой-нибудь теркат нужен и что он такое из себя представляет - можно смело говорить что он бездарен и тыкать книжкой в качестве пруфа. Хотя скорее всего будет просто грустно и хотеться уйти...
Ладно хватит абстрактной воды, пора немного рассказать о чём книга. В предисловии Хофштадтер приводит мнения разных людей о том про что эта книга. Так вот, дочитав до конца я понял что очень важного предположения там не хватает. Эта книга - о сложности!
Действительно одна из центральных концепций книги - "странные петли". Системы с обратной связью, но только между всеми уровнями, без начала. Про этакие гармошки свёрнутые в бублик. Про проблемы которые можно начать изучать откуда угодно и и никогда не закончить. Про всю эту жуть которая пугает технаря со стажем, так же как пугала всех математиков ещё 100 лет назад. А может и им до сих пор страшновато?
Сама структура книги образует петлю. Каждая глава начинается с небольшого диалога разыгрывающего очередную концепцию. Она подхватывается текстом который немного расшифровывает её и немного диалоги предыдущих глав. Каждая новая глава привносит больше понимания в предыдущие, открывает незаметные детали.
Книга рассматривает в деталях как можно всё запутать, как сама природа это делает. И вместе с тем показывает как можно что угодно распутать назад, понять, построить устойчивую модель. Например очень кратко и интересно рассказывается про синтез белков и передачу наследственной информации. Не просто о физике процесса, но о семантике разных его компонентов. Я немало времени потратил на биологию в своё время, и уверен что если в профильных книгах такое и можно найти, то в очень немногих.
Книгу стоит прочитать хотя бы ради того чтобы увидеть что даже для бесконечно сложных объектов мы всегда можем найти средства описания, способ думать о них простым и непротиворечивым способом. Найти объяснение очень глубинным свойствам используя очень примитивный базис. Автор не обещает что такие способы мышления будут очень легки, но убеждает в том что они существуют.
понедельник, 7 октября 2013 г.
Несколько простых идей по улучшения кода на Scala
Читая код людей только начавших применять язык, да и вспоминая собственный креатив двухлетней давности я часто замечаю что читаемость можно радикально улучшить использованием буквально пары конструкций по очень простым шаблонам. В этой маленькой статье я попробую ими поделиться. Но для начала надо сказать что под улучшением я понимаю упрощение чтения отдельно взятого фрагмента. Приёмы ниже не направлены на производительность, время написания или хороший дизайн в целом. Однако я берусь утверждать что они ускоряют чтение отдельных фрагментов кода.
Самое очевидное, но и самое часто забытое - for. Альтернативой ему обычно выступают цепочки из map, filter, collect, flatMap, foreach. for имеет смысл использовать почти всегда когда нужна манипуляция с составом коллекции. Во-первых он существенно легче читается при росте длины. Цепочка из 3-4 преобразующих методов находится уже на грани понимания, при записи в for-нотации эта грань ещё далеко. Во-вторых for позволяет существенно читабельнее выписывать типы связанных переменных, а компилятор частенько от нас этого требует. Например сравните:
В-третьих структура такого цикла устойчива при большинстве стандартных изменений, таких как добавление фильтров, отображений, создание сайд-эффекта вместо возврата значения.
Однако есть и ситуация когда прямое использование методов коллекций оправдано - если у нас уже есть функция фильтрации или преобразования и у неё хорошее говорящее имя. Например names.filter(wellFormed). В таком случае скорее формируется мини-DSL вокруг коллекции в целом чем набор манипуляций с отдельными элементами.
Связана с этим исключением вторая конструкция которой стоит уделить тут внимание - каррируемые функции. То есть функции представленные цепочка вызовов, каждый из которых связывает лишь часть аргументов.
Удачно сочетая методы стандартного API с именами и сигнатурами своих функций можно строить очень читабельные и производительные DSL просто на ровном месте. Там где в джаве обычно получаются циклы с телами на 200 строк.
Третья недоиспользованная возможность языка - case classes. Во-первых они позволяют с разумной перегрузкой по синтаксису создавать новые типы данных. Тип данных из 3 полей это не 50 строк кода, а одна! Их можно и нужно создавать всякий раз когда они нужны, каждый модуль на 100 строк может позволить себе иметь 10 внутренних типов с собственными именами и именами их полей и при этом быть читабельным. Во-вторых они могут быть использованы в паттерн-матчинге без дополнительных усилий.
Особенно всё описанное выше подходит для исключений, не забываем что catch - это по сути паттерн. Если наше исключение является case-классом - можно делать интересные вещи в обработке исключенией. Например вкладывать в них какие-то полезные (но не меняющие семантику данные) данные. Сравните
В какую сторону изменится выбрасывающий исключения код предлагаю додумать самостоятельно.
В завершение хочу поделиться вот этой вот замечательной книжкой: Scala By Example Раньше она висела на главной документации по языку, но сейчас почему-то исчезла оттуда. Если вы начали изучать Scala с какого-то мудрёного доклада на конференции или вводной на какой-то user group - скорее всего забыть всё услышанное и начать с этой книжки будет отличной идеей. Она является действительно кратким и очень целостным введением в язык, при этом просто полна идеями как с помощью самых простых средств писать очень выразительные программы на Scala.
Самое очевидное, но и самое часто забытое - for. Альтернативой ему обычно выступают цепочки из map, filter, collect, flatMap, foreach. for имеет смысл использовать почти всегда когда нужна манипуляция с составом коллекции. Во-первых он существенно легче читается при росте длины. Цепочка из 3-4 преобразующих методов находится уже на грани понимания, при записи в for-нотации эта грань ещё далеко. Во-вторых for позволяет существенно читабельнее выписывать типы связанных переменных, а компилятор частенько от нас этого требует. Например сравните:
sessions.foreach((s: Session) => mngr.cancel(s.id))и
for (s: Session <- sessions) mngr.cancel(s.id)
В-третьих структура такого цикла устойчива при большинстве стандартных изменений, таких как добавление фильтров, отображений, создание сайд-эффекта вместо возврата значения.
Однако есть и ситуация когда прямое использование методов коллекций оправдано - если у нас уже есть функция фильтрации или преобразования и у неё хорошее говорящее имя. Например names.filter(wellFormed). В таком случае скорее формируется мини-DSL вокруг коллекции в целом чем набор манипуляций с отдельными элементами.
Связана с этим исключением вторая конструкция которой стоит уделить тут внимание - каррируемые функции. То есть функции представленные цепочка вызовов, каждый из которых связывает лишь часть аргументов.
def hostedOn(host: Host)(node: Node) = host.addresses.contains(node.address)
def handleFailure(failed: Host) = {
val affectedNodes = allNodes filter hostedOn(failed)
...
}
Удачно сочетая методы стандартного API с именами и сигнатурами своих функций можно строить очень читабельные и производительные DSL просто на ровном месте. Там где в джаве обычно получаются циклы с телами на 200 строк.
Третья недоиспользованная возможность языка - case classes. Во-первых они позволяют с разумной перегрузкой по синтаксису создавать новые типы данных. Тип данных из 3 полей это не 50 строк кода, а одна! Их можно и нужно создавать всякий раз когда они нужны, каждый модуль на 100 строк может позволить себе иметь 10 внутренних типов с собственными именами и именами их полей и при этом быть читабельным. Во-вторых они могут быть использованы в паттерн-матчинге без дополнительных усилий.
Особенно всё описанное выше подходит для исключений, не забываем что catch - это по сути паттерн. Если наше исключение является case-классом - можно делать интересные вещи в обработке исключенией. Например вкладывать в них какие-то полезные (но не меняющие семантику данные) данные. Сравните
try {
...
} catch {
case NoDataReceivedException =>
channel.close()
showError("No data received.")
case PartialDataReceivedException =>
if (confirmDataLoss(session.received()))
channel.close()
}
и
try {
...
} catch {
case TimeoutException(received) if received == 0 =>
channel.close()
showErrorDialog(received)
case TimeoutException(received) =>
if (confirmDataLossDialog(received))
channel.close()
}
В какую сторону изменится выбрасывающий исключения код предлагаю додумать самостоятельно.
В завершение хочу поделиться вот этой вот замечательной книжкой: Scala By Example Раньше она висела на главной документации по языку, но сейчас почему-то исчезла оттуда. Если вы начали изучать Scala с какого-то мудрёного доклада на конференции или вводной на какой-то user group - скорее всего забыть всё услышанное и начать с этой книжки будет отличной идеей. Она является действительно кратким и очень целостным введением в язык, при этом просто полна идеями как с помощью самых простых средств писать очень выразительные программы на Scala.
воскресенье, 8 сентября 2013 г.
Пара слов о реальных проблемах со Scala
Как то давно я накатал изрядную стену текста на тему что хорошо в Scala и почему люди рассказывающие что на ней невозможно писать не вызывают у меня доверия: http://it-talk.org/post82738.html#p82738
Пришло время востановить баланс и немного поговорить о конкретных проблемах, которые я в ней вижу.
Первое что раздражает после перехода с Java - время компиляции. Можно сказать что от среднеполезного проекта можно ожидать времени компиляции от 2 минут. Это крайне печально. Отложенный фидбэк замедляет разработку. Для склонных отвлекаться людей вроде меня фатально замедляет; частенько запустив компиляцию я обнаруживаю себя мин через 20 читающим какую-нибудь мало относящуюся к задаче статейку. В то время как всё прогрессивное человечество минимизирует циклы работы, разработчики компилятора не занимаются этой проблемой уже много лет. fsc по моему опыту радикально ситуацию не улучшает.
Существенно лучше показывает себя sbt и волшебная ~. Однако тул это не простой, когда я следил за его развитием совместимость ломалась чуть не каждый релиз. А если надо развивать проект с историей, то смену системы сборки в серьёз никто не рассматривает. В итоге сейчас я вижу скорость работы компилятора главной проблемой scala.
Вторая серьёзная проблема - это подготовка пишущих. Часто можно слышать позитивные рассказы про так один человек взял и внедрил и вся команда в функциональном экстазе залабала в разы быстрее. Это не правда. Если дать скалу человеку, которого в универе учили на паскале а потом он 5 лет писал на джаве, вы получите кучу (в смысле втрое больше чем того требует задача) джава-кода в другом синтаксисе но компилирующегося в 8 раз медленнее. Более менее окупающее в моём понимании применение скалы требует нескольких вещей от всех кто её использует.
Во-первых это понимания почему собственно этот язык. Если люди уверены что new ArrayList/for/add лучше map, он и правда будет лучше!
Во-вторых базового навыка программирование в функциональном стиле. Ничего космического, но надо ясно видеть в коде простые паттерны роде map/filter. Не заводить переменные без повода. Понимать что функции можно параметризовать не только данными но и операциями. Уметь создавать простые АТД по мере необходимости (а не мега бины со сложной символикой null'ов в разных полях). Вообще этерпрайзие мозга сводит на нет потуги эффективно применить языки моложе 20 лет :)
По большому счёту это все серьёзные проблемы, но есть еще пара неприятностей.
Не совсем уравновешенное сообщество. В том смысле что нельзя смотреть лежащий на гитхабе код и стабильно учиться. Не устоялся стиль, не сформировались практики. Нет общепринятой границы хорошо-плохо (что на самом деле хорошо, но не в самом начале). Кто-то пишет на самом деле на хаскеле, у кого-то джава с замыканиями. Нужно очень критическое восприятие для того чтобы отбирать решения и техники для своего проекта. Пруф: http://ru-scala.livejournal.com/39341.html.
Переусложнена на мой взгляд библиотека коллекций. Множество методов преобразования с неочевидными результатами. Куча классов и методов время работы которых непросто удержать в голове.
Противоречивый, сложный фреймворк Akka. Который позиционируется как аналог OTP выполненный на JVM, но на самом деле принципиально от него отличается.
Вот вроде и всё. Ваши мысли по сабжу?
Пришло время востановить баланс и немного поговорить о конкретных проблемах, которые я в ней вижу.
Первое что раздражает после перехода с Java - время компиляции. Можно сказать что от среднеполезного проекта можно ожидать времени компиляции от 2 минут. Это крайне печально. Отложенный фидбэк замедляет разработку. Для склонных отвлекаться людей вроде меня фатально замедляет; частенько запустив компиляцию я обнаруживаю себя мин через 20 читающим какую-нибудь мало относящуюся к задаче статейку. В то время как всё прогрессивное человечество минимизирует циклы работы, разработчики компилятора не занимаются этой проблемой уже много лет. fsc по моему опыту радикально ситуацию не улучшает.
Существенно лучше показывает себя sbt и волшебная ~. Однако тул это не простой, когда я следил за его развитием совместимость ломалась чуть не каждый релиз. А если надо развивать проект с историей, то смену системы сборки в серьёз никто не рассматривает. В итоге сейчас я вижу скорость работы компилятора главной проблемой scala.
Вторая серьёзная проблема - это подготовка пишущих. Часто можно слышать позитивные рассказы про так один человек взял и внедрил и вся команда в функциональном экстазе залабала в разы быстрее. Это не правда. Если дать скалу человеку, которого в универе учили на паскале а потом он 5 лет писал на джаве, вы получите кучу (в смысле втрое больше чем того требует задача) джава-кода в другом синтаксисе но компилирующегося в 8 раз медленнее. Более менее окупающее в моём понимании применение скалы требует нескольких вещей от всех кто её использует.
Во-первых это понимания почему собственно этот язык. Если люди уверены что new ArrayList/for/add лучше map, он и правда будет лучше!
Во-вторых базового навыка программирование в функциональном стиле. Ничего космического, но надо ясно видеть в коде простые паттерны роде map/filter. Не заводить переменные без повода. Понимать что функции можно параметризовать не только данными но и операциями. Уметь создавать простые АТД по мере необходимости (а не мега бины со сложной символикой null'ов в разных полях). Вообще этерпрайзие мозга сводит на нет потуги эффективно применить языки моложе 20 лет :)
По большому счёту это все серьёзные проблемы, но есть еще пара неприятностей.
Не совсем уравновешенное сообщество. В том смысле что нельзя смотреть лежащий на гитхабе код и стабильно учиться. Не устоялся стиль, не сформировались практики. Нет общепринятой границы хорошо-плохо (что на самом деле хорошо, но не в самом начале). Кто-то пишет на самом деле на хаскеле, у кого-то джава с замыканиями. Нужно очень критическое восприятие для того чтобы отбирать решения и техники для своего проекта. Пруф: http://ru-scala.livejournal.com/39341.html.
Переусложнена на мой взгляд библиотека коллекций. Множество методов преобразования с неочевидными результатами. Куча классов и методов время работы которых непросто удержать в голове.
Противоречивый, сложный фреймворк Akka. Который позиционируется как аналог OTP выполненный на JVM, но на самом деле принципиально от него отличается.
Вот вроде и всё. Ваши мысли по сабжу?
Линкопост
Решил вот для поддержания блога живым поделиться подборкой ссылок. Это тут отнюдь не новые статьи, скорее то что отметилось актуальностью а не новизной..
Basics of the Unix Philosophy - в статье рассказывается история "философии Юникс", в форме примеров её формулировок разными людьми в разное время. Очень интересно показывается развитие идей в области дизайна программ.
The TTY demystified - подробно рассматривается архитектура TTY, то как особенности железа разных эпох влияли и продолжают влиять на устройство современных (уже исключительно виртуальных) терминалов. Очень интересная история, красивый и пугающий пример врмирования legacy.
Code's Worst Enemy - древний пост в ныне уже мёртвом блоге. Однако в связи со спецификой нынешней работы я прочувствовал его в полную силу только сейчас. В статье рассматриваются динамика и последствия раздувания кодовой базы. Очень помогает критически посмотреть на цену добавления "ещё одной фичи".
Как эффективно обучать инженеров? - изыскания небезызвестного Валкина на тему. Две очень полезные ссылки внутри.
VS Naipaul’s Seven Rules for Beginners - В продолжение темы, 7 простых правил написания текстов. Подкупает именно простотой. Вот, практикуюсь...
Jepsen - большая серия статей в которой рассматриваются проблемы поведения распределённых систем при сегментации. Интересна тем что автор предлагает простой тест, выражающих в конкретных цифрах утерянных данных и применяет его к ряду очень модных "scalable, fault-tolerant, NoSQL" баз данных. Серия ценна подробным анализом и объяснениями результатов тестов. Must read для всех практикующих модерновые БД в полях.
Java's Atomic and volatile, under the hood on x86 - подробный разбор особенностей реализации атомиков в Java. Содержит целый ряд заслуживающих внимания ссылок на смежные темы.
What is RCU, Fundamentally? - рассматривает семейство неблокирующих алгоритмов типа Read-Copy-Update и то как они поддерживаются Linux Kernel. Содержит массу примеров того как легко получить некорректный алгоритм такого типа, упустив из виду нюансы моделей памяти. Попутно даётся и обзор моделей памяти современных процессоров. Весь код на С, но не очень суровом, типичному Java-программисту было понятно.
Basics of the Unix Philosophy - в статье рассказывается история "философии Юникс", в форме примеров её формулировок разными людьми в разное время. Очень интересно показывается развитие идей в области дизайна программ.
The TTY demystified - подробно рассматривается архитектура TTY, то как особенности железа разных эпох влияли и продолжают влиять на устройство современных (уже исключительно виртуальных) терминалов. Очень интересная история, красивый и пугающий пример врмирования legacy.
Code's Worst Enemy - древний пост в ныне уже мёртвом блоге. Однако в связи со спецификой нынешней работы я прочувствовал его в полную силу только сейчас. В статье рассматриваются динамика и последствия раздувания кодовой базы. Очень помогает критически посмотреть на цену добавления "ещё одной фичи".
Как эффективно обучать инженеров? - изыскания небезызвестного Валкина на тему. Две очень полезные ссылки внутри.
VS Naipaul’s Seven Rules for Beginners - В продолжение темы, 7 простых правил написания текстов. Подкупает именно простотой. Вот, практикуюсь...
Jepsen - большая серия статей в которой рассматриваются проблемы поведения распределённых систем при сегментации. Интересна тем что автор предлагает простой тест, выражающих в конкретных цифрах утерянных данных и применяет его к ряду очень модных "scalable, fault-tolerant, NoSQL" баз данных. Серия ценна подробным анализом и объяснениями результатов тестов. Must read для всех практикующих модерновые БД в полях.
Java's Atomic and volatile, under the hood on x86 - подробный разбор особенностей реализации атомиков в Java. Содержит целый ряд заслуживающих внимания ссылок на смежные темы.
What is RCU, Fundamentally? - рассматривает семейство неблокирующих алгоритмов типа Read-Copy-Update и то как они поддерживаются Linux Kernel. Содержит массу примеров того как легко получить некорректный алгоритм такого типа, упустив из виду нюансы моделей памяти. Попутно даётся и обзор моделей памяти современных процессоров. Весь код на С, но не очень суровом, типичному Java-программисту было понятно.
среда, 26 сентября 2012 г.
Validation и исключения: совместная жизнь
Итак, после ознакомления с основными техниками применения валидаций давайте подумаем как их можно использовать в "реальном" проекте. "Реальный" проект подразумевает работу не в интерпретаторе в течении пары миллисекунд, под чутким присмотром автора, а на сервере далеко за океаном. И лишь изредка суровый незнакомый админ будет проверять не сожрала ли программа всю имеющуюся память...
Это значит,что будет происходить много чего о чём автор кода мог даже не догадываться. А о таких вещах JVM извещает код бросая исключения. Да да, возьмёт и бросит, и плевать ей на чистоту.
Ну и есть ещё один момент, не каждый алгоритм так уж легко записать, обрабатывая ошибки с помощью валидаций. Вот, например, такая штука была в какой-то момент у меня в проекте:
В общем-то ничего сложного - берём запись из БД, разбираем проверяем значения, создаём объект. При этом что-то может упасть, но что и как упало на этом уровне никого не волнует. Разбор записи надо прекращать сразу независимот от того нарушилась ли консистентность или умерло соединение с БД.
Мои основные мысли на тему Exceptions vs. Validations можно выразить таким вот набором тезисов:
И каких-то его потомков, например так:
Тогда можно легко задать функцию преобразования:
Имея на руках такую функцию можно написать систему из методов-врапперов для экранирования интерфейсных методов (зелёная рамочка) и собрать из них более или менее абстрактный модуль. Это будет выглядеть примерно так:
Здесь используется стандартный модуль control.Exception. Описывающий обработку исключений в функциональном стиле. Если бы хотелось иметь более гибкую связь исключений с нашими ошибками, например иметь несколько стратегий отображения, то именно соединяя этот модуль с разными функциями отображения мы могли бы добиться необходимой адаптивности кода.
Пользоваться этими служебными функциями можно так:
Экранируя подобным образом методы компонентов связаных с вводом-выводом (или ещё чем-то трудно обрабатываемым при помощи валидаций) мы можем создать на определённом уровне глобальный переход от одного способа работы с ошибками к другому. И стоящая выше логика может пользоваться всеми теми приятностями про которые рассказывал ранее. На картинке это уровни 'Services' и 'Controllers'.
Есть ещё один важнейший момент, который сподвиг меня на все эти эксперименты, но о котором я не говорил ранее. Дело в том, что конечной целью было построение REST сервиса, использующего протокол HTTP. Замечательной особенностью HTTP, дичайше игнорируемой всеми, является прямая поддержка сообщений об ошибках. В нём есть понятие кода ответа, что позволяет стандартным образом с детально описанной семантикой отвечать любому клиенту и быть понятым. Ещё раз подумайте, до любого клиента, хоть браузера, хоть curl можно донести что пошло не так и как с этим можно побороться... Вместо этого повсеместно практикуется '200 OK... {error: "Failed to..."}'. Зачем???!!!
Ну да, хватит о грустном. Поговорим о весёлом. А состоит оно в том что если мы представить себе, что у нас есть класс HttpResponse, позволяющий описать все ответы. То преобразование из результата R выполнения какой-то операции в HttpResponse концептуально абсолютно корректно задать как функцию Validation[R] => HttpResponse. Которая естественным образом распадается на незатейливый шаблон:
Где buildFailureResponse, как не сложно догадаться задаётся один раз на систему, а весь шаблон легко инкапсулируется в простую конструкцию. Предполагая, что у нас определены фунция-сериализатор decompose: Any=>JSONObject и преобразование из объекта ошибки в HTTP ответ errorResponse, можно написать такую функцию.
Имея операцию op: A=>ListVal[B], где A и B уже специфичные для приложения классы представления запросов и ответов, можно легко строить (нетривиальные!) обработчики запросов в одну строчку.
Здесь для примера я преобразую пришедший список результатов в пару значений длина и собственно список (ну представим что кому-то из клиентов так удобнее ;) ). В таком примере getUsersInternal имел бы тип String => ListVal[List[User]].
Такого вида функции тривиально цепляются к REST модулю Lift - не буду тратить место на пример.
Осталось предусмотреть последнюю деталь: возможность выброса исключения кодом высокого уровня. От них трудно страховаться, и глупо готовиться к ним везде в коде программы. Простое решение - перехват исключения с помощью некоторого хука, который предоставляют большинство контейнеров и фреймворков, конвертация в ошибку и преобразование уже её в ответ. В моём случае таким хуком являлся LiftRules.exceptionHandler.
Вот в общем и всё. Наверно выглядит немного сумбурно, но примерно так оно и было. Надеюсь этот пример, кому нибудь поможет или хотя бы устранит страх перед высокоуровневыми конструкциями в программировании.
Это значит,что будет происходить много чего о чём автор кода мог даже не догадываться. А о таких вещах JVM извещает код бросая исключения. Да да, возьмёт и бросит, и плевать ей на чистоту.
Ну и есть ещё один момент, не каждый алгоритм так уж легко записать, обрабатывая ошибки с помощью валидаций. Вот, например, такая штука была в какой-то момент у меня в проекте:
def readFields(rec: DBObject, ...): GzVal[DataRecord] = {
val deletedBy =
for {
userId <- get[ObjectId](rec, "deleted_by");
user <- getUserData(userId).toSuccess(MissedEntity(userId.toString, "user")) } yield user
for {
id <- get[String](rec, "_id")
content <- get[String](rec, "content")
updated <- asValidOption(get[DateTime](rec, "upd"))
//twelve more
user <- getUserById(userId, currentUserId map (new ObjectId(_)))
.toSuccess(MissedEntity(userId.toString, "user"): ValidationError)
} yield DataRecord(/*you not gonna like it*/)
}
В общем-то ничего сложного - берём запись из БД, разбираем проверяем значения, создаём объект. При этом что-то может упасть, но что и как упало на этом уровне никого не волнует. Разбор записи надо прекращать сразу независимот от того нарушилась ли консистентность или умерло соединение с БД.
Мои основные мысли на тему Exceptions vs. Validations можно выразить таким вот набором тезисов:
- Валидации позволяют очень выразительно и кратко обрабатывать ошибки в чистом коде.
- При этом используя и смешивая разные стили обработки ошибок.
- Они требуют небольшой синтаксической избыточности на каждую операцию.
- В то же время исключения часто являются вполне достойной абстракцией для обработки ошибок.
- Они создают значительную синтаксическую избыточность, зато сразу на группу операций.
Суммируя эти наблюдения с тем фактом, что неожиданные исключения всё-равно будут пролетать, я принял на вооружение следующие принципы:
- Высокоуровневые абстракции в приложении стоит строить используя валидации, однако страховать их на случай совсем внезапной ошибки.
- Тот код который легче написать с использованием исключений нужно писать с использованием исключений.
- Исключения из такого кода надо ловить и преобразовывать в валидации.
Далее я попробую описать основные детали того, как я пытался преобразовать эти принципы в работающую архитектуру. Для начала общая картина, срисованная с моего доклада в Минске.
Слева располагается достаточно грязный код, связанный с внешними службами вроде хранилища данных. Он бросает исключения как часть логики. Для него нужно выделить некоторый набор интерфейсных методов, которые перехватывают исключения и преобразовывают их в ошибки (символическая зелёная рамка). Преобразование это является по сути простой функцией, которую (не в случае бибилиотечного кода) вполне можно статически задать просто одну на систему ('Exception converter' на диаграмме).
Те, кто находятся правее написаны в более чистом стиле, игнорируют исключения (в смысле прозрачного пробрасывания наверх, а не в смысле подавления). Об их существовании вновь вспоминает лишь тонкая прослойка, между нашим кодом и внешним миром (в моём случае это был Lift, в других может быть сетевая библиотека или GUI).
Давайте рассмотрим несколько фрагментов кода, для более точного представления этих идей. Скажем для нашего HTTP сервиса из предыдущих примеров мы отказались от строки как от типа ошибки, определив такой класс:
Те, кто находятся правее написаны в более чистом стиле, игнорируют исключения (в смысле прозрачного пробрасывания наверх, а не в смысле подавления). Об их существовании вновь вспоминает лишь тонкая прослойка, между нашим кодом и внешним миром (в моём случае это был Lift, в других может быть сетевая библиотека или GUI).
abstract class Error(
val httpCode: Int,
val code: String)
И каких-то его потомков, например так:
case class MissedEntity( id: String, type: String) extends Error(404, "MISSED_ENTITY") case class InternalError( @transient Throwable cause) extends Error(cause, "INTERNAL")
Тогда можно легко задать функцию преобразования:
val dispatchException: Function[Throwable, Error] = {
case UserNotFound(name) =>
MissedEntity(name, "user")
...
// Don't remove it!!!
case t => InternalError(t)
}
Имея на руках такую функцию можно написать систему из методов-врапперов для экранирования интерфейсных методов (зелёная рамочка) и собрать из них более или менее абстрактный модуль. Это будет выглядеть примерно так:
def safeVal[T]: Catch[ReqVal[T]] =
handling(classOf[Throwable]) by { e =>
dispatchException(e).fail
}
def safe[T](block: => T): ReqVal[T] =
safeVal( block.success )
Здесь используется стандартный модуль control.Exception. Описывающий обработку исключений в функциональном стиле. Если бы хотелось иметь более гибкую связь исключений с нашими ошибками, например иметь несколько стратегий отображения, то именно соединяя этот модуль с разными функциями отображения мы могли бы добиться необходимой адаптивности кода.
Пользоваться этими служебными функциями можно так:
def editData(e: Edit):
ReqVal[Data] = safe {
//all dangerous stuff here
}
Экранируя подобным образом методы компонентов связаных с вводом-выводом (или ещё чем-то трудно обрабатываемым при помощи валидаций) мы можем создать на определённом уровне глобальный переход от одного способа работы с ошибками к другому. И стоящая выше логика может пользоваться всеми теми приятностями про которые рассказывал ранее. На картинке это уровни 'Services' и 'Controllers'.
Есть ещё один важнейший момент, который сподвиг меня на все эти эксперименты, но о котором я не говорил ранее. Дело в том, что конечной целью было построение REST сервиса, использующего протокол HTTP. Замечательной особенностью HTTP, дичайше игнорируемой всеми, является прямая поддержка сообщений об ошибках. В нём есть понятие кода ответа, что позволяет стандартным образом с детально описанной семантикой отвечать любому клиенту и быть понятым. Ещё раз подумайте, до любого клиента, хоть браузера, хоть curl можно донести что пошло не так и как с этим можно побороться... Вместо этого повсеместно практикуется '200 OK... {error: "Failed to..."}'. Зачем???!!!
Ну да, хватит о грустном. Поговорим о весёлом. А состоит оно в том что если мы представить себе, что у нас есть класс HttpResponse, позволяющий описать все ответы. То преобразование из результата R выполнения какой-то операции в HttpResponse концептуально абсолютно корректно задать как функцию Validation[R] => HttpResponse. Которая естественным образом распадается на незатейливый шаблон:
r match {
case Sucess(r) => buildSuccessResponse(r)
case Failure(f) => buildFailureResponse(f)
}
Где buildFailureResponse, как не сложно догадаться задаётся один раз на систему, а весь шаблон легко инкапсулируется в простую конструкцию. Предполагая, что у нас определены фунция-сериализатор decompose: Any=>JSONObject и преобразование из объекта ошибки в HTTP ответ errorResponse, можно написать такую функцию.
def valsToResponse[A](v: ListVal[A]): HttpResponse = v match {
case Success(res) =>
JsonResponse(decompose(rel), successCode)
case Failure(err) => errorResponse(err)
}
Имея операцию op: A=>ListVal[B], где A и B уже специфичные для приложения классы представления запросов и ответов, можно легко строить (нетривиальные!) обработчики запросов в одну строчку.
def getUsers(req: Req, namePattern: String): LiftResponse = valsToResponse {
getUsersInternal(namePattern) map { users =>
val (count, list) = users
Map("count" -> count, "items" -> list)
}
}
Здесь для примера я преобразую пришедший список результатов в пару значений длина и собственно список (ну представим что кому-то из клиентов так удобнее ;) ). В таком примере getUsersInternal имел бы тип String => ListVal[List[User]].
Такого вида функции тривиально цепляются к REST модулю Lift - не буду тратить место на пример.
Осталось предусмотреть последнюю деталь: возможность выброса исключения кодом высокого уровня. От них трудно страховаться, и глупо готовиться к ним везде в коде программы. Простое решение - перехват исключения с помощью некоторого хука, который предоставляют большинство контейнеров и фреймворков, конвертация в ошибку и преобразование уже её в ответ. В моём случае таким хуком являлся LiftRules.exceptionHandler.
Вот в общем и всё. Наверно выглядит немного сумбурно, но примерно так оно и было. Надеюсь этот пример, кому нибудь поможет или хотя бы устранит страх перед высокоуровневыми конструкциями в программировании.
Ярлыки:
achtung code,
exception,
programming,
scala,
scalaSPB,
scalaz,
validation
Подписаться на:
Сообщения (Atom)

