Pull to refresh

Функциональное программирование в среде 1С: Предприятие 8

Reading time4 min
Views5.8K
В последнее время наметилась тенденция прникновения идей функционального программирования в массы. Для меня, как программиста 1С, интереснее всего повышение уровня абстракции при работе с табличными данными. Одно дело кодировать циклы со множеством переменных, которые меняют свое значение от итерации к итерации, а через месяц надо проводить «отладку глазами» (а то и на самом деле отладчик запускать), чтобы понять как эти циклы работают. Гораздо изящнее использовать готовые отлаженные алгоритмы, которые можно применить к таблице в целом, и получить ожидаемый результат.

Год за годом кодируя похожие и не очень циклы, я проникался желанием изменить что-то к лучшему в этом унылом процессе. Первое время меня вдохновляли обощенные алгоритмы STL С++. Потом для общего развития я изучал Haskell — этот язык действительно переворачивает восприятие.

Примерно 2 года назад я начал писать библитеку универсальных функций, которые применял в повседневной работе. Практика убедила меня, что подход работает, и приносит ощутимую пользу. А совсем недавно я открыл для себя язык LINQ, который используется на платформе .NET для унифицированной работы с коллекциями, формирования SQL-запросов и других полезных вещей. Я завидую белой завистью шарперам, у которых есть такой замечательный инструмент!

Изучив библиотеку стандартных операторов запроса, которая составляет ядро LINQ, я решил написать аналогичную библиотеку для 1С Предприятия 8.

Те наработки, которые у меня были до сих пор — выкидываю в помойку, т.к. они недостаточно системны и универсальны.

Основное препятствие для создания функционально-ориентированной библиотеки — это отстутствие во встроенном языке поддержки передачи функций как параметров.

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

Я рассматривал разные варианты замены полноценных замыканий какими-либо суррогатами, вплоть до написания на встроенном языке виртуальной машины, интерпретирующей байт-код. В итоге пришел к выводу, что целесообразние всего воспользоваться возможностью языка вычислять выражения на встроенном языке, переданные в библиотеку виде строки (обоснование решения есть в проектной документации). Выглядит это так:
Отфильтровано = Таблицы.Если("_.Количество > 0",, ИсходнаяТаблица);

Символ подчеркивания обозначает параметр функции, в данном случае — строка исходной таблицы. Если функция принимает несколько параметров, они обозначаются "_0", "_1" и т.д. Функция, переданная в алгоритм, может использовать какие-то данные, известные на вызывающей стороне (контекст). Например:
Граница = 100;
Отфильтровано = Таблицы.Если("_.Количество > _к.Граница", Новый Структура("Граница", Граница), ИсходнаяТаблица);

Переменная "_к" получает значение контекста (обычно это структура), переданного вторым параметром функции Если. Для демонстрации более сложных конструкций приведу несколько надуманный пример:
СуммыЗаказовКлиентов = Таблицы.Соединение(
    "Клиент=_.Ссылка",
    "Клиент=_0.Ссылка
    |ИНН=_0.ИНН
    |Заказано = _1.Сумма",,
    Клиенты, Заказы
);

Первый парметр задает ключевые поля соединения, и преобразует имена полей первой таблицы («Ссылка») в имена полей второй таблицы («Клиент»). Второй параметр задает выражения для полей результата (Клиент, ИНН, Заказано). «Клиенты» и «Заказы» это исходные таблицы значений.

Обработка данных в таблицах значений при помощи встроенного языка — не лучшее решение задачи. Втроенный язык работает значительно медленнее SQL запросов. Мы прибегаем к встроенному языку, только если данные получены не из базы или их объем невелик, а так же для нетривиальных операций (не все можно сделать запросами).

В свою библиотеку я включил функции, которые выглядят как универсальные алгоритмы, но на самом деле формируют запрос.
Запрос = Запросы.Выбрать(
    "Спецификации.Номенклатура КАК Материал, 
    |(Заказы.Количество * Спецификации.Количество) КАК Количество",
    Запросы.Соединение(
        "Заказы", "Спецификации",
        "Заказы.Спецификация = Спецификации.Ссылка",
        Запросы.Из("Документ.ЗаказНаПроизводство.Продукция"),
        Запросы.Из("Справочник.Специфификации.ИсходныеКомплекующие")
    )
)
Результат = Запросы.Выполнить(,,Запрос);

Для чего я это сделал? Приведенный пример быстрее и надежнее сделать конструктором запроса. Читается это все равно хуже, чем обычный язык запросов. Неужели только из любви к искуству? Не только. Эта часть библиотеки предназначена для сложный запросов, которые содержат повторяющиеся элементы, или переменное количество полей, подзапросов. Такие запросы обычно формируются контакенацией строк, что приводит к необходимости следить за синтаксисом, в особенности, за расстановкой запятых и скобок. Я надеюсь библиотека в этих случаях принесет больше пользы, чем вреда, хотя на практике я такой подход еще не тестировал.

На данный момент полностью готова проектная документация и заголовки функций. К реализации функций еще не приступал, но представляю ее себе с точностью до строчки — проект проработан очень подробно. Тестирование всего массива функций может составить бОльшую трудность. Я надеюсь получить отклик от коллег. Если будут обоснованные замечания / предложения, я готов откорркетировать интерфейс библиотеки, пока еще не приступил к реализации. Готовые модули тоже собираюсь публиковать. Планирую также расширять библиотеку добавлением специфических учетных алгоритмов: распределение по базе, распределение по фифо и т.д.

Ссылки:
  1. Проектная документация на библиотеку
  2. Модуль Запросы
  3. Модуль Таблицы
  4. Модуль Массивы
  5. Спецификация библиотеки «Стандартные операторы запросов» для языка LINQ
  6. Вдохновляющая презентация о преимуществах функционального подхода на примере языка Haskell
Tags:
Hubs:
+20
Comments43

Articles