Spectral design

InBox

Опираемся от ключевой концепции, что объекты многогранны. То есть даже самый простой объект содержит столько аспектов, что нет никакой возможности описать его сразу с достаточной степенью точности. В этом суть "спектральности".

Технически функциональная парадигма тоже говорит о подобных аспектах, например монада Option добавляет аспект отсутствия значения (проверить, по крайней мере Nullable в C# выглядит так). Но они часто ассоциируются с контейнерами. А рост количества контейнеров вокруг объекта создает своеобразную матрёшку, с которой очень сложно будет управляться, поскольку возникает обилие комбинаций. Например, Nullable<Valid<Request>> Valid<Nullable<Request>>.

ADT хоть и выглядят статическо красиво, но очень настораживают, когда речь идёт о расширении приложений через плагины и расширения, чтобы реализовать OCP.

В качестве альтернативы попробуем рассмотреть конструкцию, когда у нас есть некий уникальный код объекта (скажем, инкрементный индекс) и с ним динамически могут быть ассоциироваться самые различные аспекты. Например, есть битовая карта для аспекта Option, где по индексу (index \ 8) мы можем найти байт, а по (index % 8) - бит. Получается достаточно оптимальная и компактная структура, выжимающая максимум эффективности для заданного аспекта.

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

В функциональном подходе ещё одним большим вопросом является Dependency Injection. И здесь подобные аспекты могут быть как провайдерами данных, так и предоставлять сервисы. Это будет во многом похоже на Property Injection, недостатки которого будут нивелированы Flow Manager описанным ниже. А достоинства у Property Injection в сравнении с Construct Injection есть. Во-первых, CI - это в большинстве случаев три конструкции - параметр для инъекции в конструкторе, внутреннее поле для хранения инъекции и инициализация этого поля из переданного аргумента (которую легко можно забыть). Против всего одного свойства в случае с PI. Аспектность же подобного подхода даёт много преимуществ для повторного использования.

Условный Flow Manager может пользоваться такими детальными типами, комбинируя их вместе с допонительными контролирующими выражениями, по аналогии как это делается при помощи dependent types. Например, можно сказать, что условный Unsafe<SignIn>, у которого пустой Login получает аспект исключения.

When<SignIn> .Is<Unsafe>(request => request.Login == null) .Then<ValidationException>() ; // Вариант, когда свойство определено отдельно, чтобы его могла использовать и валидация When<SignIn> .Is<Unsafe>(request => request.Login, string.IsNullOrEmpty) .Then<ValidationException>() ;

Пересечение с dependent types выглядит перспективно. Но в отличие от привычных компиляторов (Agda, Coq, Arend, etc) в runtime/testtime-реализации могут быть свои преимущества. Всё-таки время компиляции существенно влияет на скорость разработки и проверка всего кода (даже инкрементальная) не всегда нужна. В большинстве случаев мы работаем с конкретным тестом.

Попробуем рассмотреть простой HTTP-запрос SignIn. Факт появления запроса на некотором роуте приводит к возникновению аспектов SignIn+HTTP+JsonEncoded (не хотелось бы хранить SignIn до декодирования, но пока не очень понятно). Аспект HTTP можно использовать позже для рендеринга ответа.