А2: Прототип 2

Выложен второй прототип (см. папку «про2» в https://gitflic.ru/project/alekseinedoria/a2).

Это игра крестики-нолики, которая состоит из

  • инструмента «Движок»
  • инструмента «Доска»
  • и двух игроков, каждый представлен верстаком и инструментом «Игрок».

Движок запрашивает создание доски, подключает игроков и далее дергает игроков, чтобы они сделали код. Оба игрока сейчас реализованы одним инструментом (примитивным) и результат игры всегда ничья (что и должно быть на поле 3х3). Я не пытался сделать разумную стратегию игры (мне лень на это отвлекаться), так как прототип не для этого. Он показывает схему и динамическое взаимодействие.

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

В этом файле вывод программы, в начале вывода — показана схема.

В этом прототипе я ставил задачу воспроизвести (в общем) устройство Вира, но на языке обеспечивающем безопасность типов. Дальше собираюсь сделать прототип с использованием утиных интерфейсов/протоколов (duck-typing interfaces). Этот прототип будет умозрительным, так как добавлять протоколы в Тривиль я пока не собираюсь, задача, скорее, прикинуть, что нужно добавлять в компонентный язык.

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

6 комментариев


  1. Добрый день. Посмотрел второй прототип.

    Начал выписывать вопросы и по ходу дела понял, что я совершенно неправильно подходил к анализу первого прототипа.

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

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

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

    Надеюсь, в ближайшие дни смогу прислать уже конкретные вопросы и картинки по обоим намеченным аспектам.

    Ответить

    1. Добавил тексты 3-го прототипа — папка «про3», содержит:
      — схема в двух вариантах: json и условный формат
      — движок и игрок на некотором условном расширении Тривиля

      Суть прототипа: переход на декларативное описание схемы и на использование «протоколов» (duck-type interfaces). Задача: статический контроль везде где можно, возможность плавного перехода от динамики к статике.

      Не уверен, что тексты можно понять без моих пояснений, хотя какие-то комментарии там есть.
      И начал рисовать презентацию, пока мало и невнятно.

      Ответить

    2. Итак, обещанная диаграммка того компонентного дизайна, который мне удалось восстановить из кода прототипа 2
      https://github.com/iamhere2/HLCD-Researches/blob/master/Trivill/dgm/Trivill-Proto-02.png

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

      Глядя на такую диаграмму, мы сразу можем задавать множество вопросов к дизайну. Я прекрасно понимаю, что Алексей преследует несколько другие цели, чем сделать идеальный дизайн приложения крестиков-ноликов :) Но все же давайте посмотрим, что получилось, подумаем, что могло бы быть сделано лучше, и, главное — что мы могли бы сделать на уровне языка, чтобы язык подталкивал к более хорошему дизайну. Это как раз моя idée fixe, что язык должен учить разработчиков хорошему дизайну, а не вот это вот все, что мы имеем сейчас.

      Первое, что хочется заметить, это то, что дизайн не светится очевидным образом в коде и его пришлось reverse engineer’ить. В этом направлении Алексей работает, ждем его версии языка описания компонентной структуры. Я уверен, что язык должен описывать структуру очень-очень близкую к вот такием вот компонентным диаграммам, прямо таки «1 в 1» из кода в диаграмму.

      Второе, на что хочу обратить внимание — это класс «Данные». С моей т.з. это прямо отличный пример того, как современное ООП «учит плохому». Несмотря на паттерны DDD и GRAPS, мы имеем в этом классе смешение очень разных ролей — это и значение, и изменяемые данные, и что-то, что передают от одного компонента другому, и потребитель внешних интерфейсов ввода/вывода. При этом он, к тому же, вроде как принадлежит какому-то компоненту Доска, но, в то же время, отдается наружу и изменяется кем-то снаружи. Мне бы очень хотелось, чтобы в языке новой парадигмы сделать такого монстрика было бы очень сложнео и это было бы таким же моветоном как активное использование GOTO в эпоху становления структурного программирования.

      Какие возможны альтернативы?
      1. Состояние доски может быть неизменяемым значением с набором функций перехода. Это было бы в стиле ФП, и значения типа BoardState передавались бы из компонента в компонент. Вполне нормальный вариант, и конечно, в значении не должно быть обращения к вводу/выводу. Хорошо бы, чтобы в языке для этого варианта была первоклассная поддержка immutable value types, а не «классы». С запретом обращаться куда-то и менять содержимое.

      2. Состояние доски, действительно, может быть агрегатом — объектом, инкапсулирующим некоторое сложное состояние и позволяющим его менять по строгим правилам, сохраняющим некоторые инварианты. Это хороший паттерн, но опять-таки, не надо из агрегата обращаться к внешним интерфейсам, только к частям самого агрегата. Хорошо бы это тоже сделать первоклассным специализированным элементом языка вместо «опять класс, просто совсем другой».

      3. Герметичность. Это визуально видно на диаграмме, даже если она не слишком точна. Данным вроде как нужен ввод/вывод, и вроде как данные — часть Доски, но при этом не видно, чтобы Доске был нужен ввод/вывод. Мы имеем тут «протекание интерфейсов», которое хорошо было бы исключить в новой парадигме. Хорошо бы, чтобы у нас было явное отношение «дочернего компонента» (и это не то же самое, что и внещшняя зависимость!), и чтобы дочерние компоненты могли получить только те интерфейсы, которые им выдаст родительский компонент. Этот принцип имеет очень далеко идущие последствия, и неочевидным образом привносит еще одно из преимуществ ФП, можно обсудить отдельно.

      Собственно, мое предложение было бы избавиться от «классов» и заменить их на три явные специализации — «значения», «агрегаты», и герметичные компоненты с интерфейсами.

      Ну и, конечно, вводить явную схему связи компонент в язык.

      Ответить

      1. Эту схему я понимаю. Мне более привычно рисовать её в виде дерева, но для такой небольшой схемы можно и так. Замечу, что часть иерархии на схеме не видна: инструменты Игрок на верстаках Игрок1, Игрок2.

        >Я уверен, что язык должен описывать структуру очень-очень близкую к вот такием вот компонентным диаграммам, прямо таки «1 в 1» из кода в диаграмму.

        См: https://gitflic.ru/project/alekseinedoria/a2/blob?file=про3%2Fпро3.json&branch=pro4
        https://gitflic.ru/project/alekseinedoria/a2/blob?file=про3%2Fпро3.а2&branch=pro4

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

        >Мне бы очень хотелось, чтобы в языке новой парадигмы сделать такого монстрика было бы очень сложно и это было бы таким же моветоном как активное использование GOTO в эпоху становления структурного программирования.

        Согласен и в частности и в целом. Данные — это точно не пример для подражания, я несколько раз переписывал этот кусок, чтобы размер примера был минимальным, и при этом была возможность исполнения.

        Но на мой взгляд, наличие «паттернов» это скорее требование не к языку, а чему-то «над» языковому. В каком-то смысле, это должно быть расширение понятий инвариант, пред-условие, пост-условие. Впрочем, может быть и язык. Если ты можешь предложить языковый механизм (механизмы), будет очень полезно.

        >первоклассная поддержка immutable value types
        Обязательно надо + надо понятие «владелец памяти» с запретом изменения всем, кто не владелец. Об этом думают, например, здесь: https://aglang.org/

        >3. Герметичность.

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

        Ответить

    3. И отдельно — про динамическую инфраструктуру. Собственно, не буду расписывать мелкие вопросы, а задам один обобщающий.
      Праивльно ли я понимаю, что на примере такого небольшого масштаба мы не можем увидеть преимуществ от динамической связи компонент? Пока я их не вижу, но быть может, они проявятся на бОльшем масштабе? Какой нужен масштаб и какие особенности целевого приложения, чтобы увидеть преимущества динамического взаимодействия, поиска соседей по имени, парсинга команд и настроек, и т.п.?

      Ответить

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

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

        Ответить

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *