• Компилируемая конфигурация распределённой системы

      Хотелось бы рассказать один интересный механизм работы с конфигурацией распределённой системы. Конфигурация представлена напрямую в компилируемом языке (Scala) с использованием безопасных типов. В этом посте разобран пример такой конфигурации и рассмотрены различные аспекты внедрения компилируемой конфигурации в общий процесс разработки.


      Жизненный цикл конфигурации


      (english)

      Читать дальше →
    • Compilable configuration of a distributed system

        In this post we'd like to share an interesting way of dealing with configuration of a distributed system.
        The configuration is represented directly in Scala language in a type safe manner. An example implementation is described in details. Various aspects of the proposal are discussed, including influence on the overall development process.


        Overall configuration management process


        (на русском)

        Read more →
      • Тестирование белого ящика

          Разработка программ высокого качества подразумевает, что программа и её части подвергаются тестированию. Классическое модульное (unit) тестирование подразумевает разбиение большой программы на маленькие блоки, удобные для тестов. Либо, если разработка тестов происходит параллельно с разработкой кода или тесты разрабатываются до программы (TDD — test driven development), то программа изначально разрабатыватся небольшими блоками, подходящими под требования тестов.


          Одной из разновидностей модульного тестирования можно считать propery-based testing (такой подход реализован, например, в библиотеках QuickCheck, ScalaCheck). Этот подход основан на нахождении универсальных свойств, которые должны быть справедливы для любых входных данных. Например, сериализация с последующей десериализацией должна давать такой же объект. Или, повторная сортировка не должна менять порядок элементов в списке. Для проверки таких универсальных свойств в вышеупомянутых библиотеках поддерживается механизм генерации случайных входных данных. Особенно хорошо такой подход работает для программ, основанных на математических законах, которые служат универсальными свойствами, справедливыми для широкого класса программ. Есть даже библиотека готовых математических свойств — discipline — позволяющая проверить выполнение этих свойств в новых программах (хороший пример повторного использования тестов).


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


          Под катом описаны несколько подходов к тестированию сложных программ с одним входом с разной степенью сложности (вовлеченности) и разной степенью покрытия.

          Читать дальше →
        • Классы типов в Scala (с небольшим обзором библиотеки cats)

            При слове "полиморфизм" сразу вспоминается объектно-ориентированное программирование, в котором полиморфизм является одним из столпов (Полиморфизм для начинающих). (Причём, по-видимому, более важным, чем другие столпы.) Оказывается, что можно достичь сходного эффекта и другим путём, который в ряде случаев оказывается более предпочтительным. Например, с помощью классов типов можно приписать новые возможности уже существующим типам, у которых нельзя изменить предка, или, используя тип данных с несовместимыми классами, "решить" проблему множественного наследования.

            Читать дальше →
          • Строго типизированные комбинаторы для построения парсера и синтезатора естественного языка

              Известные ParserCombinator'ы и Parboiled предназначены исключительно для разбора формальных языков. Мы же решаем задачу разбора естественного языка и при этом хотим, чтобы с помощью той же грамматики можно было осуществлять синтез фраз на естественном языке, отражающих требуемую нам семантику. Было бы удобно иметь возможность описывать языковые конструкции вместе с правилами абстрагирования/конкретизации.

              Например,

              1. Преобразование числительных в число («десять» -> 10:Int)
              2. и обратно (10:Int -> «десять» («десятый», «десяток» ...))
              3. Преобразование числительных вместе с единицей измерения («десять рублей» <-> NumberWithMeasurement(10, RUB))
              4. Неполный адрес («ул. Яблочная» <-> Address(street=«Яблочная»))
              5. Адрес в пределах города («улица Яблочная дом сто двадцать три квартира сорок пять» <-> Address(street=«Яблочная», building=123, flat=45))
              6. Телефон (256-00-21 («двести пятьдесят шесть ноль ноль двадцать один») <-> NumericalSequence(256,0,0,21))

              Причём хотелось бы иметь следующие системные свойства:

              • единственность описания правил абстрагирования/конкретизации
              • строго типизированное представление семантики на всех уровнях абстракции
              • наличие альтернативных форм представления семантики и возможность повлиять на выбор формы представления семантики
              • согласование словоформ для получения фразы на чистом русском языке
              • возможность формирования вторичных структур на основе исходных правил. В частности, мы бы хотели формировать грамматики разбора, соответствующие правилам.

              Под катом — описание подхода, реализованного в библиотеке synapse-typed-expressions. Рассмотрены только числительные, но подход естественным образом распространяется на другие вышеупомянутые формальные языковые конструкции.
              Читать дальше →
              • +12
              • 6,7k
              • 2
            • Строго типизированное представление неполных данных

                В предыдущей статье «Конструирование типов» была описана идея, как можно сконструировать типы, похожие на классы. Это даёт возможность отделить хранимые данные от метаинформации и сделать акцент на представлении самих свойств сущностей. Однако описанный подход оказывается довольно сложным из-за использования типа HList. В ходе развития этого подхода пришло понимание, что для многих практических задач линейная упорядоченная последовательность свойств, как и полнота набора свойств, не является обязательной. Если ослабить это требование, то конструируемые типы значительно упрощаются и становятся весьма удобны для использования.

                В обновлённом варианте библиотеки synapse-frames исключительно просто описываются иерархические структуры данных и представляются любые подмножества таких структур.

                Читать дальше →
                • +17
                • 7,2k
                • 2
              • Конструирование типов в Scala

                  При построении многослойных («enterprise») систем часто оказывается, что создаются ValueObject'ы (или case class'ы), в которых хранится информация о каком-либо экземпляре сущности, обрабатываемом системой. Например, класс

                  case class Person(name: String, address: Address)
                  


                  Такой способ представления данных в системе обладает как положительными свойствами:
                  • строго типизированный доступ к данным,
                  • возможность привязки метаинформации к свойствам с помощью аннотаций,


                  так и некоторыми недостатками:
                  • если сущностей много, то таких классов также становится довольно много, а их обработка требует много однотипного кода (copy-paste);
                  • потребности отдельных слоёв системы в метаинформации могут быть представлены аннотациями к свойствам этого объекта, но возможности аннотаций ограничены и требуют использования reflection'а;
                  • если требуется представить данные не обо всех свойствах объекта сразу, то созданные классы использовать затруднительно;
                  • затруднительно также представить изменение значения свойства (delta).


                  Мы хотим реализовать фреймворк, позволяющий создавать новые «классы» (типы, конструкторы этих типов, объекты новых типов) инкрементно, используя наши собственные «кирпичики». Попутно, пользуясь тем, что мы сами изготавливаем «кирпичики», мы можем достичь таких полезных свойств:
                  • возможность описывать отдельные свойства сущностей (с указанием типа данных в этом свойстве и любой метаинформации, необходимой приложению, в форме, подходящей именно для этого приложения);
                  • возможность оперировать со свойствами экземпляров строго типизированным образом (с проверкой типов на этапе компиляции);
                  • представлять частичную/неполную информацию о значениях свойств экземпляра сущности, пользуясь объявленными свойствами;
                  • создавать тип объекта, содержащего частичную информацию о свойствах экземпляра сущности. И использовать этот тип наравне с другими типами (классами, примитивными типами и др.).

                  Читать дальше →
                  • +12
                  • 8,2k
                  • 8
                • Обработка событий в реальном масштабе времени с помощью SynapseGrid

                    Занимались мы как-то обработкой аудио на Java с помощью сложных алгоритмов. Каждый кусочек аудио должен был пройти длинную цепочку обработки (20-50 алгоритмов разной степени сложности). Потоки аудио поступали параллельно, алгоритмы работали параллельно, и завершались в разные моменты. Некоторые алгоритмы нуждались в разной степени буферизации. Из кусочков аудио извлекалась информация повышающегося уровня абстракции, то есть начиная с какого-то уровня уже шло не аудио, а извлечённая информация об этом аудио.

                    Всё хозяйство должно было работать в рамках одного экземпляра приложения, но при этом должно было быть несколько вложенных почти независимых очень похожих контейнеров для клиентского кода (типа Bean'ов).

                    С самого начала мы не ставили задачу всеобщей унификации, и решали в каждой части системы по своему. Где-то использовали потоки для длительных задач, где-то создавали цепочки вызовов, где-то — модель подписки. Так как система была довольно большой, то практически все известные способы декомпозиции и обработки были задействованы в той или иной степени. Потом мы обнаруживали общность и реализовывали похожие решения в разных частях системы. А потом изобрели первую версию того, что сейчас мы называем система контактов или SynapseGrid.
                    Как мы изобрели систему контактов SynapseGrid
                    • +11
                    • 3,8k
                    • 2