V>>Это не просто COM. Это философия сродни реактивному/агентному программированию, только с помощью КОП. То бишь, это парадигма над парадигмой компонент. ))) V>>На самом деле единственно разумное зерно — это специальные связи компонент. Я когда-то назвал такое "пинами" в аналогичных рассуждениях: http://www.rsdn.ru/forum/philosophy/1676773.1
ARK>Спасибо, очень интересная точка зрения. Мне нравится, концептуально очень изящно. Может быть есть какие-то реализации или языки с подобной философией?
Из имеющихся подобных языков — только для разработки аппаратуры. ))
Можно взять Немерле и на нем попробовать сделать подобную библиотеку для софта.
Re[11]: Заметка: Компонентная философия и реализация
Здравствуйте, AlexCab, Вы писали:
AC>Скромно напоминаю: мы обсуждаем компонентную модель(и средства создания компонентов) хоть и похожую на COModel, но всё таки отличающеюся от неё.
Ну так вот отличия именно модели ты так и не смог продемонстрировать, все время скатываешься с модели на детали реализации типа ключевых слов в языке или токенов методов.
AC>>>QueryInterface делает именно запрос, он не выполняет приведение. AVK>>В чем разница? AC>Как разница между словами "получить" и "преобразовать". Как бы, похоже, но не то.
Это все софистика, а не ответ на вопрос.
AC>>>А мои мифические connect/disconnect _не_ делают ни запроса, ни приведения. Они делают соединение/разъединение интерфейса. AVK>>В чем разница? AC>Например, в процессе: преобразование интерфейса сложнее(состоит из большего числа логических действий) чем получение интерфейса, а подключение интерфейса сложнее чем преобразование.
Это не ответ на вопрос, а игра словами.
AC>Или например, в исходном и конечном состоянии: в первом случае изначально есть один интерфейс, в конце другой; во втором изначально нет ни одного интерфейса(есть только GUID интерфейса), в конце есть интерфейс(в виде таблицы методов); в третьем(для connect) изначально есть имя интерфейса, имя/хендл компонента и первая(экспортируемая) часть реализации интерфейса, в конце есть вторая(импортирования) часть реализации интерфейса, плюс выполнена функция-событие подключения.
Это — детали реализации. К модели это не относится.
AVK>>В момент JIT компиляции она это делает, а не при непосредственном выполнении. Причем уже в IL фигурирует токен метода, а не его метаданные. Метаданные хранятся отдельно. И не читайте русскую википедию, она гадость. AC>Токен проверяется при каждом вызове?
Я непонятно написал? "В момент JIT компиляции она это делает". При выполнении там обычный статический или виртуальный вызов, как в обычном нативном языке типа С++.
... << RSDN@Home 1.2.0 alpha 5 rev. 66 on Windows 8 6.2.9200.0>>
AC>>Скромно напоминаю: мы обсуждаем компонентную модель(и средства создания компонентов) хоть и похожую на COModel, но всё таки отличающеюся от неё. AVK>Ну так вот отличия именно модели ты так и не смог продемонстрировать, все время скатываешься с модели на детали реализации типа ключевых слов в языке или токенов методов. AC>>Или например, в исходном и конечном состоянии: в первом случае изначально есть один интерфейс, в конце другой; во втором изначально нет ни одного интерфейса(есть только GUID интерфейса), в конце есть интерфейс(в виде таблицы методов); в третьем(для connect) изначально есть имя интерфейса, имя/хендл компонента и первая(экспортируемая) часть реализации интерфейса, в конце есть вторая(импортирования) часть реализации интерфейса, плюс выполнена функция-событие подключения. AVK>Это — детали реализации. К модели это не относится.
Где заканчивается модель, и начинаются детали реализации?
Или опиши модель, а я опишу отличия.
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re[13]: Заметка: Компонентная философия и реализация
Здравствуйте, AlexCab, Вы писали:
AC>Где заканчивается модель, и начинаются детали реализации?
А ты сам не в состоянии границу провести? Модель это обязательные контракты компонентной инфраструктуры, идентичные в разных реализациях. То, что от реализации к реализации меняется моделью не является.
... << RSDN@Home 1.2.0 alpha 5 rev. 66 on Windows 8 6.2.9200.0>>
AC>>Где заканчивается модель, и начинаются детали реализации? AVK>А ты сам не в состоянии границу провести? Модель это обязательные контракты компонентной инфраструктуры, идентичные в разных реализациях. То, что от реализации к реализации меняется моделью не является.
Ok, для обсуждаемой модели компонентно-ориентированного программирования, навскидку обязательны(т.е. без этого модель(а точнее любую её реализацию) невозможно будет использовать в полной мере, для разработки ПО, описанными в заметке способами):
1)Компоненты имеющие состояние и поведение.
2)Сборки.
3)Интерфейсы, в таком виде как они описаны выше.
4)Потоки внутри компонентов(упоминашиеся выше).
Остальное, описанное в заметке, будучи реализованным как-то иначе, думаю, существенно не повлияет на основные возможности модели(сборка из компонентов, агентное/реактивное программирование etc.).
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re[15]: Заметка: Компонентная философия и реализация
Здравствуйте, AlexCab, Вы писали:
AC>1)Компоненты имеющие состояние и поведение. AC>2)Сборки. AC>3)Интерфейсы, в таком виде как они описаны выше. AC>4)Потоки внутри компонентов(упоминашиеся выше).
Ну то есть берем дотнет, добавляем к нему простенький фреймворк (в идеале спецязык) и средства реактивного программирования и получаем то что ты хочешь. И это все? В чем новизна идеи?
AC>Остальное, описанное в заметке, будучи реализованным как-то иначе, думаю, существенно не повлияет на основные возможности модели(сборка из компонентов, агентное/реактивное программирование etc.).
Потоки внутри компонентов это и есть вариация на тему реактивного программирования.
... << RSDN@Home 1.2.0 alpha 5 rev. 66 on Windows 8 6.2.9200.0>>
AVK>Ну то есть берем дотнет, добавляем к нему простенький фреймворк (в идеале спецязык) и средства реактивного программирования и получаем то что ты хочешь. И это все?
Ну да! Только не .NET, а JVM и, не фреймворк, а полноценный ЯП(например как Scala) с встроенными средствами для реактивного программирования. AVK> В чем новизна идеи?
В собственно подходе(описанному во второй главе заметки) к программированию в целом, и к созданию и использованию компонентов в частности. А ЯП и рантайм нужны чтобы, для программиста, реализация этого подхода на практике была максимально комфортной и эффективной. AVK>Потоки внутри компонентов это и есть вариация на тему реактивного программирования.
Нет, эти потоки (выполнения) для агентного программирования. Для реактивного — потоки значений с возможностью организации очередей между компонентами.
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re[10]: Заметка: Компонентная философия и реализация
Здравствуйте, AlexCab, Вы писали:
AC>1)Грубо говоря(так как это может быть реализовано и без всяких таблиц методов), подключающийся компонент передаёт другому таблицу экспортируемых методов и получает от другого таблицу импортируемых(экспортируемых другим) методов, т.е. компоненты обмениваются таблицами(а не один передаёт другому как в COM).
Сократи содержимое обоих таблиц до 1-го вхождения, и получишь базовую модель для реактивного программирования. )
То бишь, в одном интерфейсе будет всего один сигнал. Итого, у компонента будет не много сигналов на каждый интерфейс, а много интерфейсов, где в каждом всего 1 сигнал. Но! Подобные интерфейсы должны быть в виде экземпляров, а не в виде уникальных контрактов... Ууупс!!! )) Поэтому модель контрактов современного ООП на реактивное программирование ни разу не ложится — слишком высока связанность для этого.
Тут правильно сказали, что можно смотреть идеологию DirectShow. Что удобно в такой архитектуре — развязываются руки в относительно подключений компонент друг к другу. Почему? Из-за того, что кол-во сигналов обычно невелико, т.е., имея в базисе сигналы (т.е. ориентируя архитектуру на сигналы, вернее на топологию их прохождения), мы получаем очень и очень низкую связанность компонент при всё еще существующей типобезопасности.
Re[8]: Заметка: Компонентная философия и реализация
Здравствуйте, AndrewVK, Вы писали:
AVK>Притом, что это семантический эквивалент QueryInterface.
Не совсем. Это семантический эквивалент на механику вокруг IConnectionPoint/IConnectionPointContainer, а так же их IDispatch-эквивалент. Это аналог джавовских листенеров событий. Всё это "красиво" встроено в язык VB/VBA, но в С++, Джаве и дотнете аналогичное только ручками.
AC>> Оно лишь, даёт возможность скрыть/показать часть полей объекта.
AVK>Приведение к интерфейсу к полям вообще никакого отношения не имеет — интерфейс поля не содержит, только методы. И операция приведения семантически означает запрос определенного контракта с контролем его наличия. Т.е. ровно то же самое, что делает QueryInterface и твои мифические connect/disconnect. Причем этот вариант существенно более гибок, так как не требует реактивности в обязательном порядке (но при этом вполне позволяет ее реализовать при желании — можешь посмотреть на Reactive Extensions в дотнете для примера).
Дык, реактивность требует некоторой поддержки. Например, VB превращает листенеры в нечто гораздо более "реактивное" за счет упрятанной в язык всей механики. В итоге, можно оперировать контрактами обратного вызова "поэлементно", т.е. так, как будто бы каждый callback независим от других.
AC>>К тому же каждый будет реализовывать это всё по своему, в общем будет адъ и погибель, и компонентное будущее та и не наступит. AVK>Проснись, все что ты хочешь уже есть.
Оно есть в ООП, ес-но. Но нет поддержки в мейнстриме.
Re[11]: Заметка: Компонентная философия и реализация
Здравствуйте, vdimas, Вы писали: AC>>1)Грубо говоря(так как это может быть реализовано и без всяких таблиц методов), подключающийся компонент передаёт другому таблицу экспортируемых методов и получает от другого таблицу импортируемых(экспортируемых другим) методов, т.е. компоненты обмениваются таблицами(а не один передаёт другому как в COM). V>Сократи содержимое обоих таблиц до 1-го вхождения, и получишь базовую модель для реактивного программирования. )
Можно определить интерфейс с одним импортируемым и одним экспортируемым методом, но, думаю, запрещать определять больше методов не очень хорошая идея. V>То бишь, в одном интерфейсе будет всего один сигнал. Итого, у компонента будет не много сигналов на каждый интерфейс, а много интерфейсов, где в каждом всего 1 сигнал. Но! Подобные интерфейсы должны быть в виде экземпляров, а не в виде уникальных контрактов... Ууупс!!! ))
Много интерфейсов — плохо. Это почти тоже самое что ООП, в котором много методов. В КОП, одна из главных задач интерфейсов — упорядочивание и упрощение связей компонентов(а значит и системы в целом). Что достигается объединением логически связынных методов и полей в один интерфейс, и запретом доступа к ним, если компонент не поддерживает данный интерфейс и интерфейс не подключён, в отличии от ООП где любой объект в любое время может вызывать любой метод другого объекта.
К тому же станет невозможно писать в ООП стиле, что сделает переход к компонентному программированию сложнее для программистов. V>Поэтому модель контрактов современного ООП на реактивное программирование ни разу не ложится — слишком высока связанность для этого.
Раскрой пожалуйста мысль. V>Тут правильно сказали, что можно смотреть идеологию DirectShow. Что удобно в такой архитектуре — развязываются руки в относительно подключений компонент друг к другу. Почему? Из-за того, что кол-во сигналов обычно невелико, т.е., имея в базисе сигналы (т.е. ориентируя архитектуру на сигналы, вернее на топологию их прохождения), мы получаем очень и очень низкую связанность компонент при всё еще существующей типобезопасности.
Т.е. один пин должен мочь принимать несколько типов сигналов?
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re[9]: Заметка: Компонентная философия и реализация
Здравствуйте, vdimas, Вы писали:
V>Не совсем. Это семантический эквивалент на механику вокруг IConnectionPoint/IConnectionPointContainer
По заявлениям ТС, реактивность не есть обязательный признак. Так что нет.
V>, а так же их IDispatch-эквивалент. Это аналог джавовских листенеров событий. Всё это "красиво" встроено в язык VB/VBA, но в С++, Джаве и дотнете аналогичное только ручками.
Аналогичное что? С шарпе IConnectionPoint превращается в обыкновенное событие. При желании можно завернуть в IObservable.
V>Дык, реактивность требует некоторой поддержки
Какой?
... << RSDN@Home 1.2.0 alpha 5 rev. 66 on Windows 8 6.2.9200.0>>
AC>Глобальное состояние системы хранится во всех функциях и вытекает изо всех функций.
Более точно состояние в ФП хранится в локальных переменных по всему стеку вызова. То бишь, в состояние в ФП входит состояние стека и положение указателя команд.
ООП/императив отличается тем, что состояния делят на целевые и промежуточные. Проектирую обычно только целевые состояния, а проходы по промежуточным стараются делать как можно более безопасными (в идеале — транзакционными). В этом смысле в ФП все состояния — промежуточные. ))
Re[4]: Заметка: Компонентная философия и реализация
Здравствуйте, vdimas, Вы писали:
V>Из имеющихся подобных языков — только для разработки аппаратуры. )) V>Можно взять Немерле и на нем попробовать сделать подобную библиотеку для софта.
Понял.
Подумалось еще вот что. А как в вашей модели дело обстоит с приемом сигналов в произвольные моменты времени? По идее, компонент может применяться в многопоточном окружении (он сам не знает, где применяется, поэтому рассчитывает на худшее). Получается, внутри будет куча всяких локов и прочих синхронизаций? Или же состояния у компонента вообще не может быть в принципе, и каждый выход — просто функция от всех входов? (Наверное, так и есть. )
Re[12]: Заметка: Компонентная философия и реализация
Здравствуйте, AlexCab, Вы писали:
AC>>>1)Грубо говоря(так как это может быть реализовано и без всяких таблиц методов), подключающийся компонент передаёт другому таблицу экспортируемых методов и получает от другого таблицу импортируемых(экспортируемых другим) методов, т.е. компоненты обмениваются таблицами(а не один передаёт другому как в COM). V>>Сократи содержимое обоих таблиц до 1-го вхождения, и получишь базовую модель для реактивного программирования. ) AC>Можно определить интерфейс с одним импортируемым и одним экспортируемым методом, но, думаю, запрещать определять больше методов не очень хорошая идея.
Нужен всего один метод. Идея нормальная — ведь в отсутствии таблицы получишь уменьшение косвенности на 1 (если сам делаешь рантайм).
Одна сторона (выходная) этот метод вызывает, другая сторона — подставляет функтор (пин) для вызова.
V>>То бишь, в одном интерфейсе будет всего один сигнал. Итого, у компонента будет не много сигналов на каждый интерфейс, а много интерфейсов, где в каждом всего 1 сигнал. Но! Подобные интерфейсы должны быть в виде экземпляров, а не в виде уникальных контрактов... Ууупс!!! )) AC>Много интерфейсов — плохо. Это почти тоже самое что ООП, в котором много методов.
Это если смотреть на интерфейсы как на типы. А ты смотри на них как на экземпляры. И тогда пусть их будет хоть сотни — это не мешает ни чему.
AC>В КОП, одна из главных задач интерфейсов — упорядочивание и упрощение связей компонентов(а значит и системы в целом). Что достигается объединением логически связынных методов и полей в один интерфейс, и запретом доступа к ним, если компонент не поддерживает данный интерфейс и интерфейс не подключён, в отличии от ООП где любой объект в любое время может вызывать любой метод другого объекта.
Мммм... сорри, но вынужден отослать к OLE/ActiveX. В реальности, никакого упрощения НЕТ.
1. Обычно интерфейсы не самодостаточны, а взаимодействуют со многими другими интерфейсами, если посмотреть на протокол некоей системы целиком. И тут начинает вылазить кривизна интерфейсов как типов:
2. "Вхождения" многих интерфейсов пересекаются, то бишь ф-ии/кортежи, составляющие интерфейсы, порой дублируют друг друга.
3. Интерфейсы служат тормозом к развитию. Для добавления новых членов в кортеж ф-ий, составляющих интерфейс, тупо создают новый IOldName2 или IOldNameEx и т.д., что не требуется в случае наличия необходимых "пинов".
И да. Никто тебе не запрещает делать "мета/пины", т.е. те, которые выдают как результат компоненты (кортежи других пинов). В общем, по мне проблема интерфейсов в КОП — в их уникальности, т.к. сейчас интерфейс равен типу в языке, что провоцирует на сильную связанность и дублирование содержимого интерфейсов. Нужна экземплярность.
Вот пример из электроники:
— многие пины у совершенно разных компонент совместимы друг с другом.
— когда речь о функциональности, то компонент целиком отвечает за функциональность (контракт на поведение). Т.е. реализация компонент разная, контракт один.
А что мы имеем сейчас? Сейчас контракт обслуживает сугубо интерфейсную (стыковочную) часть дизайна, а не поведенческую. То бишь, происходит ровно наоборот, под один и тот же интерфейс мы подставляем разные компоненты именно для целей разного их поведения. Ну дык! Давай те же перестанем делать вид, буд-то не понимаем этого, т.е. давайте оставим типизированный контракт сугубо для целей "стыковки" компонент, а не для целей управления семантикой... бо это ересь, т.к. компилятор не в состоянии контроллировать правильность семантики реализованного контракта. И чего тогда париться относительно "сложных" интерфейсов? Семантику пусть задают сами компоненты, а максимально простые интерфейсы пусть обеспечивают наименьшую связанность. Один метод в интерфейсе — меньше не бывает. ))
Еще пример разрыва в логике при реализации КОП сверху ООП: для того, чтобы вызвать совместимый с ранее известным некий сигнал (например, в электронике пусть будет тактовый сигнал), нам необходимо знать тип целого большого интерфейса!!! ОК, пусть система типов предлагает как вариант позвать еще один некий известный нам чуть более общий интерфейс ITactable... А что делать если у нас два тактовых входа? Ууупс???
AC>К тому же станет невозможно писать в ООП стиле, что сделает переход к компонентному программированию сложнее для программистов. V>>Поэтому модель контрактов современного ООП на реактивное программирование ни разу не ложится — слишком высока связанность для этого. AC>Раскрой пожалуйста мысль.
ИМХО, чем мощнее кортеж ф-ий, составляющих контракт, тем менее гибко его применение. Или раскрыть термин "высокая/низкая связанность"?
V>>Тут правильно сказали, что можно смотреть идеологию DirectShow. Что удобно в такой архитектуре — развязываются руки в относительно подключений компонент друг к другу. Почему? Из-за того, что кол-во сигналов обычно невелико, т.е., имея в базисе сигналы (т.е. ориентируя архитектуру на сигналы, вернее на топологию их прохождения), мы получаем очень и очень низкую связанность компонент при всё еще существующей типобезопасности. AC>Т.е. один пин должен мочь принимать несколько типов сигналов?
Нет, один пин обслуживает один тип сигнала. Просто не должно быть ограничений на конкретный тип этого сигнала.
Ну и синтаксис соединения и маппинга портов хотелось бы максимально простой:
component C1 {
in X : int;
var Z : int;
out Y : int = X * Z;
C1(z : int) : Z(z) {}
};
component C2 {
in X : int;
out Y : int;
var c1 : C1;
C2(z : int) : C1(z) {
X => c1.X;
c1.Y => Y;
}
};
component Main {
out Ya, Yb : int;
in X : int;
var a, b : C2;
Main() : a(42), b(43) {
X => a.X;
a.Y => Ya;
X => b.X;
b.Y => Yb;
}
}
===========
Я нечто подобное ковыряю на досуге... Периодически встают разного рода вопросы конфликта синтаксиса...
Re[13]: Заметка: Компонентная философия и реализация
Здравствуйте, vdimas, Вы писали:
V>Вот пример из электроники: V>- многие пины у совершенно разных компонент совместимы друг с другом. V>- когда речь о функциональности, то компонент целиком отвечает за функциональность (контракт на поведение). Т.е. реализация компонент разная, контракт один.
V>А что мы имеем сейчас? Сейчас контракт обслуживает сугубо интерфейсную (стыковочную) часть дизайна, а не поведенческую. То бишь, происходит ровно наоборот, под один и тот же интерфейс мы подставляем разные компоненты именно для целей разного их поведения. Ну дык! Давай те же перестанем делать вид, буд-то не понимаем этого, т.е. давайте оставим типизированный контракт сугубо для целей "стыковки" компонент, а не для целей управления семантикой... бо это ересь, т.к. компилятор не в состоянии контроллировать правильность семантики реализованного контракта. И чего тогда париться относительно "сложных" интерфейсов? Семантику пусть задают сами компоненты, а максимально простые интерфейсы пусть обеспечивают наименьшую связанность. Один метод в интерфейсе — меньше не бывает. ))
Хм, в таком случае "семантику" для компонента в целом задать будет нельзя. Нечто вроде утиной типизации выходит — если набор пинов у двух компонентов подходит, то они эквивалентны. Т.е. какой-нибудь вентиль с одним входом и одним выходом можно будет легко спутать с другим, по "смыслу" совершенно иным. Хотя хз, может это и не так страшно...
V>Я нечто подобное ковыряю на досуге... Периодически встают разного рода вопросы конфликта синтаксиса...
Какие конфликты, например?
Re[13]: Заметка: Компонентная философия и реализация
V>Нужен всего один метод.
Т.е. взаимодействие через интерфейс может быть только однонаправленным? V>Это если смотреть на интерфейсы как на типы. А ты смотри на них как на экземпляры.
Т.е. компонент может создавать/разрушать свои интерфейсы? V>И тогда пусть их будет хоть сотни — это не мешает ни чему.
Анализ/отладка системы? V>1. Обычно интерфейсы не самодостаточны, а взаимодействуют со многими другими интерфейсами, если посмотреть на протокол некоей системы целиком. И тут начинает вылазить кривизна интерфейсов как типов:
Это баг старых вариантов КОП. V>2. "Вхождения" многих интерфейсов пересекаются, то бишь ф-ии/кортежи, составляющие интерфейсы, порой дублируют друг друга.
В смысле в одном интерфейсе или в нескольких интерфейсах одного компонента? V>3. Интерфейсы служат тормозом к развитию. Для добавления новых членов в кортеж ф-ий, составляющих интерфейс, тупо создают новый IOldName2 или IOldNameEx и т.д., что не требуется в случае наличия необходимых "пинов".
Т.е. расширение интерфейса компонента будет выполнятся за чёт добавление новых пинов? V>А что мы имеем сейчас? Сейчас контракт обслуживает сугубо интерфейсную (стыковочную) часть дизайна, а не поведенческую. То бишь, происходит ровно наоборот, под один и тот же интерфейс мы подставляем разные компоненты именно для целей разного их поведения.
по идее(даже в том же COM'е) контракт должен включать не только соглашение о собственно интерфейсе, но и соглашение о семантике интерфейса, в т.ч. и том как дожен "вести" себя владелец интерфейса при взаимодействии через этот интерфейс. V>Один метод в интерфейсе — меньше не бывает. ))
В модели КОП из заметки, в интерфейсе может быть не одного метода, для случая когда нужно только установить факт подключения/отключения. V>Или раскрыть термин "высокая/низкая связанность"?
Да. V>Я нечто подобное ковыряю на досуге... Периодически встают разного рода вопросы конфликта синтаксиса...
Интересно, опиши пожалуйста(лучше с картинками и комментированым псевдокодом), что из себя будет представлять компонент с пинами, каковы возможности пинов и, как будет выполнятся взаимодействие компонентов, в общих чертах.
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re[14]: Заметка: Компонентная философия и реализация
Здравствуйте, AlexRK, Вы писали:
ARK>Хм, в таком случае "семантику" для компонента в целом задать будет нельзя.
Её и сейчас задать нельзя. Я потому и предлагаю не делать вид, будто можно.
Семантика пусть задается так, как она сейчас задаётся — вербально. В виде доки или ТЗ на компонент, например. )))
Ну и никто не мешает эти же пины представлять в виде типизированного "разъема", в этом случае "разъем" будет полным аналогом имеющегося на сегодня интерфейса/контракта. Каждый компонент в любом случае будет представлять из себя некий специфический разъем, просто из одних разъемов будет крайне удобно строить другие — в этом цель. Речь лишь о том, кто какие типы "видит". Например, в месте программы, где описывается порт-маппинг (т.е. соединение 2-х пинов), необходимо знать типы (разъемы) обоих компонент, без этого невозможно организовать типобезопасный код. Другое дело, что целевой пин мог придти транзитивно сколь угодно "далеко" через точно такой же порт-маппинг внутри описанных типов. Просто, помимо удобного синтаксиса "стыковки", удобным получается отуствие лишнего уровня косвенности по мере вложения компонент друг в друга. В электронике тоже простое прохождение сигнала по проводу пусть сквозь сколько угодно уровней абстракций дизайна ничего ведь не стоит... А в ООП сейчас стоит и очень некисло...
ARK>Нечто вроде утиной типизации выходит — если набор пинов у двух компонентов подходит, то они эквивалентны.
Да, один можно воткнуть вместо другого. В дотнете можно подать некий функтор (предикат, например) в огромное кол-во библиотечных методов. Надо лишь, чтобы этот функтор отвечал сигнатуре (считай — разъему). Но ведь кроме как коду функтора неизвестно, что этот он делает внутри. Я лишь предлагаю по такому принципу строить дизайн целиком, а не только отдельные библиотечные методы.
==========
Возвращаясь к утиной типизации — в ПО ситуация неплохая, в отличие от, ведь сами сигналы в ПО могут быть куда как более специализированы, чем в электронной области. Никто не мешает в виде сигнала использовать сколько угодно сложный пользовательский тип. Тут (повторюсь к посту рядом) было бы неплохо жестко отличать типы-компоненты от типов-данных. (Отдельная серьезная тема, но которая может потенциально разгрести часть бардака в современном ООП, где данные и объекты замешаны в одну кашу, и в итоге граф-"вычислитель" постоянно трансформируется в ходе работы, что лишает его детерминированности и возможности оптимизаций).
ARK>Т.е. какой-нибудь вентиль с одним входом и одним выходом можно будет легко спутать с другим, по "смыслу" совершенно иным. Хотя хз, может это и не так страшно...
Ес-но не страшно, т.к. у нас должна быть принципиальная возможность построения одних компонент из других.
V>>Я нечто подобное ковыряю на досуге... Периодически встают разного рода вопросы конфликта синтаксиса... ARK>Какие конфликты, например?
Да хочется вообще минимализма, т.е. чтобы маппинг портов указывать не отдельно, как я показал, а прямо во время объявления пина/компонента. Ну и я так без фанатизма особо пока...
Стоит задача не столько реализовать некую готовую систему, сколько изобрести синтаксис, который самого удовлетворил бы. )) Компилятор-то потом в какой-нить мейнстримовый язык (или даже байт-код) написать не проблема.
Re[15]: Заметка: Компонентная философия и реализация
Здравствуйте, vdimas, Вы писали:
V>Её и сейчас задать нельзя. Я потому и предлагаю не делать вид, будто можно. V>Семантика пусть задается так, как она сейчас задаётся — вербально. В виде доки или ТЗ на компонент, например. )))
В мейнстрим-языках нельзя, но в теории можно. Вон в Rust что-то там пытаются на практике сделать с typestate.
Кстати, я там выше спрашивал — в вашей модели, как я понимаю, компоненты принципиально без состояния (каждый выход — функция всех входов)? Т.е. понятие "последовательность вызовов" бессмысленно?
V>Ну и никто не мешает эти же пины представлять в виде типизированного "разъема", в этом случае "разъем" будет полным аналогом имеющегося на сегодня интерфейса/контракта.
Вот тут не очень понял, что выставляется наружу в виде портов — некий набор примитивных типов + можно выставлять другие компоненты?
V>Другое дело, что целевой пин мог придти транзитивно сколь угодно "далеко" через точно такой же порт-маппинг внутри описанных типов.
В смысле, выход внутреннего компонента "пробрасывается" напрямую на выход родительского?
V>Никто не мешает в виде сигнала использовать сколько угодно сложный пользовательский тип. Тут (повторюсь к посту рядом) было бы неплохо жестко отличать типы-компоненты от типов-данных.
Ну, данные наверное тоже можно считать примитивными "компонентами".
Подумал сейчас, возникла еще пара соображений.
Во-первых, "возмущение" по вашей системе, возникшее по сигналу извне, будет передаваться не мгновенно (как в теории), а за какое-то время. Что должно произойти, если реакция на прошлое возмущение еще не завершилась, а уже поступил новый сигнал? Завершаем все вычисления или прерываем и начинаем заново? В принципе, можно и так, и так сделать, наверное...
Во-вторых, как должно обстоять дело с обработкой ошибок? Предположим, в компоненте один из выходов задает функцию с ограниченной областью определения. Если функция на входных параметрах не определена, то что делаем? На один выход выставляем "еррор", а на другой — некое дефолтное значение результата? В таком случае можно и забыть проверить первый выход, получается аналог кодов возврата — ответственность на программисте. Либо как вариант — на выходе некий алгебраический тип данных Empty | Some a? Тут другой риск — эти нуллабельные типы будут пролезать во все интерфейсы и засорять их...
Во, и еще вопрос — как вы предполагаете избегать "коротких замыканий"? По идее можно ведь сделать цикл с кучей компонентов в цепочке и не заметить этого.
Re[16]: Заметка: Компонентная философия и реализация
Здравствуйте, AlexRK, Вы писали:
V>>Её и сейчас задать нельзя. Я потому и предлагаю не делать вид, будто можно. V>>Семантика пусть задается так, как она сейчас задаётся — вербально. В виде доки или ТЗ на компонент, например. )))
ARK>В мейнстрим-языках нельзя, но в теории можно.
Да всё-равно не выйдет. Программа целиком и есть семантика.
ARK>Вон в Rust что-то там пытаются на практике сделать с typestate.
Я еще не смотрел, но почему-то уверен, что когда посмотрю, то за пару минут придумаю, как без проблем удовлетворить любой контракт, но при этом нарушить исходную семантику. ))
ARK>Кстати, я там выше спрашивал — в вашей модели, как я понимаю, компоненты принципиально без состояния (каждый выход — функция всех входов)? Т.е. понятие "последовательность вызовов" бессмысленно?
Это зависит от модели расрпостранения сигналов. Если синхронная модель — то последовательность вызовов имеет смысл. Если асинхронная, то тоже имеет, но уже по-другому (реакция лишь на изменения значения, а не на факт поступления 2-в-1-м — сигнала+события).
Я, вообще-то за то, чтобы компоненты могли (при надобности) иметь состояние.
Просто я считаю, что порядок в императиве можно навести только через иерархическу систему типов, где типы, отвечающие за поведение, имели бы фиксированную при создании (иммутабельную) структуру. Мутабельные пусть будут только данные. Я уже писал выше:
... бардака в современном ООП, где данные и объекты замешаны в одну кашу, и в итоге граф-"вычислитель" постоянно трансформируется в ходе работы, что лишает его детерминированности и возможности оптимизаций.
В электронике такое разделение происходит естественным образом — схема неизменна (до следующей перепрошивки, по крайней мере, если речь о матрице вентилей), но данные (сигналы + состояния ячеек памяти) — изменяемы.
Ну и еще такой момент в предложенной системе — отсутствие гонок. Не может два и более выхода работать на один вход. Иначе это КЗ и компилятор ругается. Скажем так, понять этот момент сразу будет сложновато... Это совсем другие навыки дизайна, чем принятые в ИП сегодня.
V>>Ну и никто не мешает эти же пины представлять в виде типизированного "разъема", в этом случае "разъем" будет полным аналогом имеющегося на сегодня интерфейса/контракта. ARK>Вот тут не очень понял, что выставляется наружу в виде портов — некий набор примитивных типов + можно выставлять другие компоненты?
Наружу выставляется кортеж пинов. Такой кортеж вполне себе тип.
Насчет выставлять другие компоненты как значения пинов — я соображения уже высказал: это путь к точно такому бардаку, который уже есть де-факто. Боюсь, без понятия "фабрики" не обойтись всё-равно, но использование фабрики весьма специфично — она используется только в момент создания "вычислителя".
V>>Другое дело, что целевой пин мог придти транзитивно сколь угодно "далеко" через точно такой же порт-маппинг внутри описанных типов. ARK>В смысле, выход внутреннего компонента "пробрасывается" напрямую на выход родительского?
"Пробрасывается" не значение, а сам пин в момент соединения компонент. Значение потом идёт напрямую от источника к приемнику, игнорируя уровни иерархии дизайна.
Это как делегирование подписывания на событие в дотнете:
Такое делегирование процедуры соединения пинов может быть сколько угодно глубоким... Но из-за объемистого и недекларативного синтаксиса на практике этим инструментом редко пользуются как основным инструментом дизайна (хотя он представляет из себя удобный трюк).
V>>Никто не мешает в виде сигнала использовать сколько угодно сложный пользовательский тип. Тут (повторюсь к посту рядом) было бы неплохо жестко отличать типы-компоненты от типов-данных. ARK>Ну, данные наверное тоже можно считать примитивными "компонентами".
Хотите об этом поговорить? Я бы с удовольствием. Тут самое время раскатать в блин понятие "ссылочного типа" и его зачастую некорректное применение к типам, отвечающим за данные. И всё потому, что компилятор позволяет что угодно.
ARK>Подумал сейчас, возникла еще пара соображений. ARK>Во-первых, "возмущение" по вашей системе, возникшее по сигналу извне, будет передаваться не мгновенно (как в теории), а за какое-то время. Что должно произойти, если реакция на прошлое возмущение еще не завершилась, а уже поступил новый сигнал? Завершаем все вычисления или прерываем и начинаем заново? В принципе, можно и так, и так сделать, наверное...
Всю схему "кто-то" должен пересчитывать. Этот кто-то должен раздавать компонентам вычислительные ресурсы. Принцип раздачи может быть очень разный.
Например: каждый пин будет содержать буфер-защелку. Когда на одном (любом) входе компонента будет обнаружено событие подачи сигнала, то компонент будет поставлен в очередь к шедуллеру. Когда до компонента дойдет очередь, то он может пересчитать свои выходы от всех измененных к моменту получения вычислительных ресурсов входов. В этом смысле логический вычислительный поток каждого компонента постоянно сидит в ожидании входных сигналов. Т.е. мы имеем строгую агентную среду, где не может быть гонок внутри компонент.
Логику можно усложнить. Например, реагировать не на факт подачи сигнала, а лишь на изменение входного значения. Если состояния такого автомата будут устойчивы, т.е. значение ф-ии переходов f(s, x) = s, то это будет асинхронный автомат. Кстати, нейронные сети с обратными связями так и работают — у них есть период затухания после возмущений. Как только сеть стабилизируется, т.е. войдет в устойчивое состояние, то вычисления закончены. Прямо отсюда должно быть понятно, как комбинировать программные модели синхронных и асинхронных автоматов. Сигнал об окончании вычислений асинхронного автомата может быть событием для зависимого синхронного.
Кстате, для эрудиции, базовый в схемотехнике RS-триггер — это классический асинхронный автомат, то бишь, автомат, выполненый на "чистых" функциях — логических вентилях. Просто в схеме есть обратные связи. Вот тебе откровение №1 — земля на самом деле круглая ячейка памяти — это чистая ф-ия + обратная связь. ))
Прямо отсюда должно быть понятно, почему я не сильно отличаю императивный и функциональный подход. Подобные устойчивые системы могут быть гораздо более сложными, чем RS-триггер. Могут состоять из кучи абсолютно чистых ф-ий, но при этом обладать изменяемым состоянием. Достаточно в ФП-программе суметь создать, например, циклическую структуру данных — и ву а ля, вот тебе изменяемое состояние из бесконечно считаемых по кругу ф-ий. Текущее устойчивое значение ф-ии на каждом витке вполне можно принять за состояние (как это есть в RS-триггере). Например, в упрощенном виде такая циклическая структура дана в виде монады IO в Хаскеле (при том, что её зацикливание выполняется внешним, по отношению к программе, вычислителем).
ARK>Во-вторых, как должно обстоять дело с обработкой ошибок?
В условиях детерминированной структуры вычислителя — проще простого. Ошибка — это состояние. Исключительных ситуаций не бывает (мы ведь не можем разрушть недостроенный дизайн). Если компонент может сигнализировать об ошибке — пусть у него будет под это специальный пин.
ARK>Предположим, в компоненте один из выходов задает функцию с ограниченной областью определения.
Если речь о зависимых типах, то мы просто не сможем подать на такой входной пин неподходящий выходной. Система типов не даст.
ARK>Во, и еще вопрос — как вы предполагаете избегать "коротких замыканий"? По идее можно ведь сделать цикл с кучей компонентов в цепочке и не заметить этого.
Это цикл, а не короткое замыкание. Короткое замыкание — это когда гонки. А цикл — это очень полезная структура. ))