Scala Future

2 февраля 2016

Some time ago I had a chance to work with Scala. It is really refreshing after Java. I’ve switched to Java from Python, and it still feels like you need to write a lot of code to do simple things. So when I’ve heard that Martin Odersky himself is going to present the future of Scala on the meetup 7 minutes walk from my office, it was a no-brainer for me to go.

Some key takeaways are:

  • Scala grew in popularity a lot over last few years, which is great.
  • In mid-2016 2.12 release is planned. It will be optimized for Java 8 to run faster.
  • IMG_3443

  • After 2016: Scala 2.13 will be focused on the libraries.
    1. The plan is to revamp collections to make them easier to use, make them more in-line with Spark, and provide better lazy collections (views).
    2. Another idea is to split the Core and Platform libs for a better modularity.
    3. Finally, there is an initiative to build Scala on LLVM.
  • DOT calculus: a proven foundation for the language. It is possible now to make and prove formally the language statements.
  • IMG_3445

  • Dotty is the new Scala compiler to be built on DOT. So far dotty is twice smaller and at the same time twice faster than the current compiler (even without optimizations).
  • IMG_3446

  • Language changes:
    IMG_3447

    1. Trait parameters (basically, abstract classes will stay only for Java interoperability).
    2. Early definitions will be removed.
    3. Intersection classes (union types to be a lightweight alternative to Either): eliminated least upper bound class explosion.
  • Scala center will be the foundation for Scala development.

Sounds pretty exciting.

Хорошие подкасты

21 июля 2015

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

Мои фавориты:
1. Software Engineering Radio (на английском) — интересные гости, очень разные темы, от NodeJS и ZooKeeper до дизайна архитектуры и SBT.
2. DevZen (на русском) — тоже куча полезной информации, более низкоуровневая и практическая, чем в SER. Напросился в гости в 49-й выпуск :)

Decoupling A Monolithic Server Application

25 апреля 2015

Написали статью по результатам недавних переделок Decoupling A Monolithic Server Application

Интерпретатор своими руками с помощью Graph-talk

25 ноября 2014

Хочу рассказать о том, как сделать интерпретатор с помощью библиотеки Graph-talk. В качестве примера будет использоваться замечательный язык Brainfuck.

Грамматика языка

Всего в языке имеется 8 команд:

  • «>» — переход к следующей ячейке памяти;
  • «<» — переход к предыдущей ячейке памяти;
  • «+» — увеличить значение в текущей ячейке на 1;
  • «-» — уменьшить значение в текущей ячейке на 1;
  • «.» — вывести значение текущей ячейки;
  • «,» — ввести значение извне в текущую ячейку
  • «[» — начало цикла, выполнить содержимое внутри цикла, если в текущей ячейке не 0;
  • «]» — конец цикла, вернуться на начало с учетом вложенности.

Собственно, это все. Изначально имеется 30 000 ячеек памяти. «Hello, World!» на этом милейшем языке выглядит так:

++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++
.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.
——.———.>+.>.

 

Основные понятия graph-talk

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

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

Есть 4 типа понятий:

  1. Простое понятие — лист дерева с некоторым названием (класс Notion);
  2. Активное понятие — простое понятие, которое вызывает некоторую функцию при его обработке (ActionNotion);
  3. Составное понятие — содержит в себе несколько других понятий в указанном порядке (ComplexNotion);
  4. Выборочное понятие — аналог оператора «select», составное понятие, которое содержит в себе одно из нескольких понятий (SelectiveNotion).

Понятия связаны между собой отношениями 4-х типов:

  1. Простая связь — соединяет вместе два понятия, одно из которых называется Subject, а другое — Object (класс Relation);
  2. Направленная связь — позволяет переходить от одного понятия к другому, если выполняется заданное условие (NextRelation);
  3. Активная связь — направленная связь, которая вызывает функцию при ее обработке (ActionRelation);
  4. Распознающая связь — направленная связь, которая «поглощает» обработанный текст, если он совпадает с ее условием (ParsingRelation);
  5. Циклическая связь — направленная связь, которая повторяет выбранное понятие указанное число раз (LoopRelation);

Для обработки используется несколько типов процессов (Process) с разными возможностями. Для данного примера нам будет нужен процесс распознавания (ParsingProcess). ParsingProcess получает на вход текст и стартовую вершину графа для обработки. Затем он начинает обход графа, передавая имеющуюся у него информацию о тексте элементам графа. В зависимости от типа элемента он получает «ответы» с указаниями о дальнейших действиях, например: перейти к следующему элементу, выполнить функцию, обработать кусок текста и так далее. В конечном итоге весь входной текст оказывается обработан при помощи распознающих связей.

Моделирование процесса интерпретации

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

Для того чтобы реализовать цикл, его тело помещается внутрь соответствующего составного понятия, которое будет повторять содержимое до тех пор, пока значение текущей ячейки будет не 0. Это реализуется с помощью циклической связи (LoopRelation). На рисунке внизу показан цикл, который будет печатать содержимое текущей ячейки до тех пор, пока оно больше нуля:

Цикл

Для непосредственного выполнения команд реализуются соответствующие функции, которые будут затем исполняться виртуальной машиной Brainfuck. После распознавания команды вызов функции помещается в граф программы как активное понятие. Когда входной текст полностью обработан, процесс обхода переходит на граф программы и начинает его выполнение. Вот как будет выглядеть граф программы «,[-.]» — ввода и печати содержимого ячейки, пока оно больше 0:

Граф интерпретатора

Как видно из рисунка, сам процесс интерпретации («Interpreter») состоит из обработки исходного кода («Source», будет одинаковым для всех программ на языке Brainfuck) и результирующей программы («Program»).

Исходный код состоит из команд («Commands»), которых может быть сколько угодно и которые мы будем продолжать искать до тех пор, пока текст не кончится. Каждая команда может быть одной из 8 команд языка, пробелом или чем-то другим (то есть ошибкой). Выбор команды осуществляется при помощи выборочного понятия («Command») и распознающих связей (ParsingRelation), каждая из которых «съедает» соответствующий текст при совпадении условия. Распознающие связи ведут к активным понятиям, функции которых помещают новые активные понятия, которые будут выполнять функции языка, в граф результирующей программы.

Несколько интересных моментов:

  1. Связь «пробел» в качестве условия использует регулярное выражение. При наличии символов пробела все они будут удалены из текста.
  2. Связь «ошибка» помечена как связь по умолчанию («default») для понятия «Command». Это означает, что процесс перейдет на нее только в том случае, если все другие связи не сработают.
  3. Для поддержки вложенности циклов необходимо «прикреплять» новые команды не к самому верхнему понятию программы, а к понятию «Top», которое зависит от уровня цикла. Для этого используется стек понятий «Top», при создании нового цикла в него помещается новый элемент, а при окончании цикла элемент извлекается и новые команды начинают подключаться на уровень выше.

Полный код примера находится здесь.

Кроме интерпретатора, в нем приводится модель процесса преобразования кода на Brainfuck в код на Python, что значительно упрощает чтение текстов на этом эзотерическом языке.

How To Cook Spellcheckers, или Проверка орфографии — это просто

19 ноября 2014

Моя прошлогодняя лекция, на русском :)

Выбор лицензии для open source проекта

23 июня 2014

Недавно возникла необходимость выбрать лицензию для своего open source проекта. Вот полезный сайт, на котором объясняется разница между лицензиями.

Backend Engineer Wanted!

17 января 2014

Редкая возможность поработать в успешной продуктовой компании, создавшей продукт мирового класса — то есть у нас, в Grammarly :)
Вакансия: Backend Engineer, relocation в Киев.

Пример работы с естественными языками (Natural Language Processing) на Python

21 декабря 2013

Хорошая статья с примерами и полезными ссылками. Традиционно NLP задачи реализовывались на Java, но Python приобретает все большую популярность в этой области.

Testing Binary File Upload on Jetty

4 декабря 2013

I was developing a small web server using Jetty and Apache Commons FileUpload component when I faced the problem with automated testing of the upload functionality. Jetty has a very nice framework to do tests: ServletTester and HttpTester.

ServletTester tester = new ServletTester();
tester.setContextPath("/");
tester.addServlet(TestingHandler.class, "/");
tester.start();

HttpTester request = new HttpTester();
HttpTester response = new HttpTester();

request.setMethod("GET");
request.setURI("/");

response.parse(tester.getResponses(request.generate()));
assertEquals(200, response.getStatus());

My test had to upload a binary file to check the server-side processing, but I’ve found this is not an easy thing to do. I’ve realized that HttpTester class generate() method produces a String. Okay, let’s use BASE64 to encode the file! But this would not work, because FileUpload supports only «application/octet-stream» content type. Fortunately, FileUpload is an open source product, so I’ve just created a child class of HttpTester with my new generateBytes() method that did the trick. Basically, it is a slightly modified version of HttpTester.generate():

public static class HttpTesterBytes extends HttpTester {
    public HttpTesterBytes() {
        super();
        setHeader("Host", "tester");
    }

    public ByteArrayBuffer generateBytes() throws IOException {
        Buffer bb=new ByteArrayBuffer(32*1024 + (_genContent!=null?_genContent.length:0));
        Buffer sb=new ByteArrayBuffer(4*1024);
        ByteArrayOutputStream2 out = new ByteArrayOutputStream2();
        StreamEndPoint endp = new StreamEndPoint(null, out);

        HttpGenerator generator = new HttpGenerator(new SimpleBuffers(sb, bb),endp);

        if (_method!=null) {
            generator.setRequest(getMethod(),getURI());
            if (_version==null)
                generator.setVersion(HttpVersions.HTTP_1_1_ORDINAL);
            else
                generator.setVersion(HttpVersions.CACHE.getOrdinal(HttpVersions.CACHE.lookup(_version)));
            generator.completeHeader(_fields,false);
            if (_genContent!=null)
                generator.addContent(new View(new ByteArrayBuffer(_genContent)),false);
            else if (_parsedContent!=null)
                generator.addContent(new ByteArrayBuffer(_parsedContent.toByteArray()),false);
        }

        generator.complete();
        generator.flushBuffer();
        return new ByteArrayBuffer(out.toByteArray());
    }
}

To prepare the bytes I’ve made a helper function:

public final static String CONTENT_BOUNDARY = "<--(*)(*)-->"; // Spaceship!

/**
 * Get the binary representation of the file upload
 * @param fieldName     field name to use in the form
 * @param filename      file to load
 * @return              bytes array with file written
 * @throws IOException
 */
public static byte[] getFileRequestContent(String fieldName, String filename, boolean finalize) throws IOException {
    String content = "--" + CONTENT_BOUNDARY + "\r\n" + "Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + filename + "\"\r\n"+
            "Content-Type: application/octet-stream;\r\n\r\n";

    File f = new File(filename);
    byte[] bytes = Files.toByteArray(f);

    ByteArrayOutputStream stream = new ByteArrayOutputStream2();
    stream.write(content.getBytes());

    stream.write(bytes);

    String finish = "\r\n";
    if (finalize)
        finish += "--" + CONTENT_BOUNDARY + "--\r\n\r\n";

    stream.write(finish.getBytes());
    stream.flush();

    return stream.toByteArray();
}

And now how it looks in the test:

HttpTesterBytes request = new HttpTesterBytes();
HttpTester response = new HttpTester();

request.setMethod("POST");
request.setHeader("Content-Type", "multipart/form-data; boundary=" + CONTENT_BOUNDARY);

request.setContentBytes(getFileRequestContent("data_file, "test/Nocturne.jpg"));

response.parse(tester.getResponses(request.generateBytes()).array());

assertTrue(response.getMethod() == null);
assertEquals(200, response.getStatus());
assertTrue(response.getContent().contains("cute!"));

PyCharm бесплатно

26 сентября 2013

Вчера пришла новостная рассылка от JetBrains, в которой говорится о том, что теперь есть бесплатная версия PyCharm — Community Edition. Я сам уже три года как пользуюсь Professional Edition, и это замечательный продукт. А для Java я пользуюсь IntelliJ IDEA :) Очень рекомендую тем, кто пишет на Python, и еще не знаком с PyCharm, попробовать ее. Есть встроенная поддержка Django, virtualenv, Git/HG, тестирования и покрытия кода тестами.

← Предыдущие записи