OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Пятница, 29 Март, 2024 01:22

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 94 ]  На страницу Пред.  1, 2, 3, 4, 5  След.
Автор Сообщение
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 00:03 

Зарегистрирован: Четверг, 08 Май, 2008 19:13
Сообщения: 1447
Откуда: Киев
Илья Ермаков писал(а):
Не компонента нет, а архитектуры нет.
https://yadi.sk/i/u6K6PETHS22OsQ

Это - и среда, и приложение, границы нет. Рядом один и тот же документ-форма и в дизайн-режиме, и в боевом. Комбинация всего со всем.
Я в курсе. Когда, я отвечал на Ваш вопрос про плотную работу, я имел ввиду именно плотную работу, а не то, что я не знаю Blackbox. Вы считаете, что для показанного нужна рефлексия или без неё будет сильно сложней?

Цитата:
Хотя концептуально в плане UI они так до сих пор и не вкурили опыт Оберона.
Всё они скурили, им такое просто не нужно. Посмотрите на типичное веб приложение - там внутри динамичности сверх всякой меры, метапереход на метапереходе, а что на выходе? Подобие Blackbox или подобие того, что выдаёт QT?

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 13:30 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
Comdiv, давайте предметно:

1. чем конкретно плоха автоматическая сериализация/десериализация на базе рефлексии для специальным образом помеченных (или специальным образом не помеченных) полей структур данных? Какая именно абстракция при этом протекает? Как рукописный код позволит этого избежать?

2. чем конкретно плох механизм вызова команд через рефлексию, при том, что команда определяется, допустим, по тому, что первым параметром является commands.Context? Какая абстракция при этом протекает и как рукописный код позволит этого избежать?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 13:45 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
Кстати, говоря о рефлексии и динамизме, нужно вспоминать не лисп и смолтолк, и не только Java и Delphi, а Visual Basic, FoxPro, MS Access, SQL (любой) и Unix.

И ещё - насчёт веб технологий. В веб-браузере есть кнопка F12, которая позволяет фальсифицировать веб-страницу на стороне клиента, например, генерировать липовые отчёты или скачивать защищённые от скачивания ролики из контактика. Можно дальше много рассуждать о значении этого.

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 15:21 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
А если говорить про общее, то почти все абстракции в ИТ протекают (и, наверное, неспроста).
ООП уничтожает модульность, FFI уничтожает безопасность, в Си дырявые указатели, общая память рушит идеи по корректной параллельности. В оконном интерфейсе есть дыры (даже в чекбоксе я позавчера осознал одну серьёзную проблему, которая ставит под вопрос его применимость во многих случаях). Если весь флот дыряв, то нужно оценивать его не по отсутствию протекания, а по
практической эффективности. Выбор именно рефлексии в качестве объекта атаки выглядит весьма и весьма произвольным. Она гораздо честнее многих других вещей.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 15:32 
Аватара пользователя

Зарегистрирован: Пятница, 11 Май, 2007 21:57
Сообщения: 1488
Откуда: Украина, Киев
budden писал(а):
Она гораздо честнее многих других вещей.
И не видно каких-то особых препятствий для надёжной её реализации.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 15:36 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
Вот забыл ещё прекрасный пример протекающей абстракции - typescript.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 16:24 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Comdiv писал(а):
Blackbox. Вы считаете, что для показанного нужна рефлексия или без неё будет сильно сложней?


Я считаю, что для показанного нужен "режим интерпретации": например, когда считанная из формы-как-документа строка используется для доступа к нужной переменной и т.п. Это невозможно сделать до времени выполнения. Потому что во время компиляции неизвестно, что в документе я буду линковать к каким сущностям программы.

Рефлексия - это и есть замена интерпретатору в компилируемых языках.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 19:34 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 473
budden писал(а):
D_S__ писал(а):
Илья Ермаков писал(а):
Отказ от рефлексии - архитектурно шаг назад.

Наверное, лучше говорить не о полном отказе, а о необходимости ограничения такой возможности. В Java с 9 версии уже так просто не помахаешь рефлекшеном из-за внедрения модулей.

И неплохо бы предметно. Т.е. для таких-то случаев рефлексию хорошо использовать, а для каких-то - нет.

Имхо, первичный критерий верно отмечен -- для прикладного программирования рефлексия не должна нарушать модульность. Здесь на форуме как раз был доклад по поводу модульности Java, где эта проблематика затронута в т.ч. (теперь в Java выпрыгнуть из рефлексии невозможно, и ужиться с модулями, по сути то -- лишь по принципу "уж как есть..."):
https://forum.oberoncore.ru/viewtopic.php?f=155&t=6421&start=80#p109236
budden писал(а):
Допустим, даже рефлексия почти всегда - это зло.

В принципе то, вменяемые исключения есть. К примеру, выше Delphi был не мало упомянут, где динамические возможности ограниченной рефлексии не позволяют выпрыгнуть за пределы ООП (во всяком случае, в те старые времена, когда Delphi ещё не был испорчен Net-ом, я не в курсе насчёт всех современных наворотов). В ББ, вроде бы, возможности также ограничены, но я глубоко не знаком с ББ-проблематикой.

А вот, к примеру, здесь как-то неоднозначно отмечено:
https://forum.oberoncore.ru/viewtopic.php?f=26&t=5031&view=previous#p11933
AVC писал(а):
...
В Обероне основной инструмент расширения -- модульность.
Geniepro писал(а):
Кстати, Илья, вот Вы часто упоминаете метапрограммирование на Компонентном Паскале, а не могли бы Вы продемонстрировать какой-нибудь типичный пример его использования, что бы все сразу ахнули и сказали: "Да, определённо в этом что-то есть..." ;о)

Как иллюстрация возможностей метапрограммирования вполне подошло бы введение обработки исключений (как раз на заданную тему :) ).
Zero-Overhead Exception Handling Using Metaprogramming (1996)
http://oberon2005.ru/paper/p_ex.pdf

Публикация по ссылке в цитате недоступна, есть, напр, здесь:
https://www.researchgate.net/publication/221512809_Zero-Overhead_Exeption_Handling_Using_Metaprogramming

По сути это пример, как любая кухарка "взламывает" модульную статику распространением "инъекции", при использовании которой возникает возможность исполнения произвольной, даже вложенной, процедуры. В данном случае принципиальное отличие от той же Java лишь в отсутствии масштабной промышленной эксплуатации, прежде всего, в качестве "ынтырпрайз-серваков". В результате чего нет критики.
budden писал(а):
А что делать в остальных случаях? Полезность отладочной печати никто не оспаривал, и её очень естественно можно сделать через рефлексию.

Всё же, если средства нарушают "тезаурус" языка моделирования, то либо этот "тезаурус" противоречивый и что-то не то в консерватории, либо это средства иного технологического языка (напр., для подсистемы обеспечения runtime или/и разработки) или языка на ином системном уровне, где "полнейшая" рефлексия пригодна, а то и необходима. В последнем случае, видимо, нужны некие правила разговора между представителями разных языковых сред, мол в рамках некоего условного ВИР, здесь недавно представленного, необходимо распределять "права доступа" к возможностям, может быть как раз через упомянутые выше RESTRICT.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 19:40 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 473
Илья Ермаков писал(а):
В том, что многое можно и хорошо перенести на этап компиляции (только не ценой раздувания синтаксиса языка мета-средствами аля в С++ или Scala или Haskell) - я с Вами согласен. Не отменяя рефлексию как мощный мета-уровень в архитектуре.

Всё же, без явной поддержки "мета" в системе типизации метапереход представляется не в полном смысле, или не формальный. Может быть в шаблонной магии и навороченной параметризации полиморфизма есть избыточная сложность, но само наличие таких средств не избыточно по сути. Если не ошибаюсь, в ББ, например, нет хотя бы аналога "класса классов" (class of class) из той же Дельфи -- метаклассы или метатипы, тип как указатель на тип (через них создаются объекты с использованием рефлексии). Выше в теме дискуссия про формальную верификацию хоть и размытая, но в целом не беспочвенна. Если гипотетически предположить внедрение методологии вроде SPARK, то понадобятся средства описания типов, точнее почва для формальных предикатов как требования о возникающих структурах. Здесь "низкоуровневое API" как "ассемблер" доступа к рефлексии не подойдёт, необходимы рассуждения на более высоком уровне непосредственно о типах. Понадобится в дополнение специализированный слой предикатов, частично с дублированием средств (т.е. в языке имеются конструкции выражения типов, операторы их тестирования и пр., теперь часть семантического слоя продублируется и в виде API).
Ну..., проблематика аля SPARK уж более "высокого полёта" и широкая, чем "мета", на данный момент пусть без акцента на ней. Однако, "разнобой" (разные средства для, фактически, идентичных явлений) и частичное семантическое дублирование в технологическом языке возникает и в случае "мета на этапе компиляции" на фоне работы с runtime-рефлексией. Я детально с потенциальной методикой не знаком, предполагаю исходя от нечто вроде "умных макросов" (в данном случае вообще может возникнуть собственный язык в языке -- т.е. не ликвидация лишнего, а наоборот ...):
https://forum.oberoncore.ru/viewtopic.php?f=30&t=5366&start=20#p100049

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

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

Упомянутые выше метатипы в Delphi видятся как некое фундаментальное зерно в качестве попытки снять разнородность в "раздувании синтаксиса", и, в целом, в раздувании понятийного аппарата в тезаурусе языка. В самой Delphi концепцию не развили (даже можно допустить, что не успели, затем начался Net...). Напрашивается обобщение для метатипов как универсальное метапредставление, в т.ч. и как аналог проекций типов (смежное математическое понятие рядом со структурами) в функциональных языках. И полиморфные параметры типов по сути также являются теми же метатипами, с такими же возможностями. Причём "шаблоны" должны быть предназначены как и для compiletime-вычислений, так и для runtime-динамики.

Попробую, в порядке пятничного бреда, направление мысли в общих чертах на примерах...


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 19:43 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 473
По сути метатип (пусть как "TYPE OF ...") это явный runtime-идентификатор типа или его некий тег (при применении в runtime-части алгоритма), имеющий некое значение, важное лишь для компилятора и runtime-системы. В некотором смысле метатип можно оценить как базис для концепции "токенов", здесь на форуме фрагментно когда-то представленную (если я примерно верно представляю суть концепции).

Псевдокод:
Код:
TYPE
  Rec = EXTENSIBLE RECORD
    a: INTEGER; b: REAL
  END;
  ExRec = RECORD(Rec)
    c: INTEGER;
  END;

VAR
  tr: TYPE OF Rec;  (* "указатель" на сам тип, в данном случае на Rec *)
  pr: POINTER TO Rec;
...
  (* определяем значение для "указателя" типа, в данном случае на наследника/расширение *)
  tr := ExRec;

  (* Создание и использование объекта определенного выше типа.
     Операция NEW является функцией -- см. далее *)
  pr := NEW(tr);
  pr.a := 456;
  ...

Компилятор отслеживает совместимость для метатипов (как и для всего прочего).

Для динамического поиска нужного типа среди "зарегистрированных" в системе (если предусматривается "доступ" к "правильной" рефлексии) по имени или иному параметру (м.б. с возможностью динамической загрузки модулей/пакетов и т.п.) пусть используется идентификатор модуля (с целью минимального задействования явного процедурного API, если уж рефлексия является частью системы типизации):
Код:
MODULE MyModule;

TYPE
  Rec = EXTENSIBLE RECORD
    a: INTEGER; b: REAL
  END;

VAR
  tr: TYPE OF Rec;
  pr: POINTER TO Rec;
...
  tr := MyModule['ExRec']; (* динамический поиск типа *)
  IF ASSIGNED(tr) THEN
    pr := NEW(tr);
  END;


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 19:45 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 473
Метатип на внутренние поля структуры:
Код:
TYPE
  Rec = RECORD
    a: INTEGER; b: REAL
  END;

VAR
  r1, r2: Rec;
  ra: TYPE OF INTEGER IN Rec;  (* тег типа внутри структуры *)
  rb: TYPE OF REAL IN Rec;
  i: INTEGER;
...
BEGIN
  (* инициализация тегов *)
  ra := Rec.a;
  rb := Rec.b;

  (* использование тега как проекции типа или как указатель на член структуры *)
  ra[r1] := 10;
  rb[r1] := 345.55;
  (* ... что эквивалентно: *)
  r1.a := 10;
  r1.b := 345.55;

  ...
  i := ra[r2];
  ...

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

Создание объекта с динамической структурой:
Код:
VAR
  r: ANY;
  ra: TYPE OF INTEGER;
  rb: TYPE OF REAL;
  i: INTEGER;
...
  (* после создания объекта ANY-указатель условно обладает RTTI-информацией
     для удовлетворения задач тестирования типа, доступа к данным и прочего согласно семантике языка *)
  r := NEW([ra, rb]);

  ra[r] := 10;
  rb[r] := 345.55;
 
  i := ra[r];
  ...

Массив метатипов (любой -- статический или динамический) интерпретируется как эквивалент статического типа, т.е. выше:
[ra, rb] => RECORD INTEGER, REAL END

Одиночное значение также может быть интерпретировано структурно: ra => RECORD INTEGER END


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 19:49 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 473
Здесь на форуме где-то уже были ссылки на древнюю реализацию "STL" для Паскаля в исходном "дельфийском" духе. "STL" могла бы быть в таком стиле:
Код:
TYPE
  (* тип в качестве ключа для map-контейнера *)
  Key = RECORD
    a, b: INTEGER;
  END;

  (* "значение" для map *)
  ValData = RECORD
    s: STRING;
  END;

VAR
  (* полиморфный тип-контейнер (см. далее), определенный в модуле С *)
  m: C.MultiMap;
  k: TYPE OF Key;
  v: TYPE OF ValData;
  h: Handle; (* тип как виртуальный указатель на элемент контейнера *)
...
  (* инициализация контейнера *)
  m := NEW(k, v); 

  (* Начало ввода нового map-ключа. Контейнер возвращает некую ссылку на управляемую
    им область памяти *)
  h := m.beginAppend();

  (* Установка значений map-ключа через "теги", с прямым доступом (как бы через ссылку)
    к памяти в h *)
  k[h].a := 12;
  k[h].b := 3445;
  (* "сохранение" ключа. Контейнер управляет размещением элемента на основе "ключа", может обновить
    необходимые структуры, если это нечто вроде multiindex и пр. *)
  h := m.postAppend(h);

  (* установка "значения" *) 
  v[h].s := 'asdsa asd as';

  (* краткая форма операции. Здесь тип аргумента у "append" определен как Key *)
  v[m.append([1234, 456])] := 'fdsfdsf';

  (* или с использованием тега k, здесь применяется оператор аппликации "(...)",
     возвращающий значение "подконтрольного" типа *)
  v[m.append(k(1234, 456)] := 'fdsfdsf';
  ...

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

Пример вектора без применения тегов:
Код:
VAR
  (* полиморфный тип из "родового" модуля, предусматривающий перегрузку необходимых операторов *)
  v: C(INTEGER).Vector;

  i: INTEGER;
  h: Handle;
...
  (* здесь операция индексации такая же, как и у обычных массивов, с целочисленным порядком доступа к элементам *)
  v[1] := 43;
  i := v[123];

  (* перегруженная операция индексации для типа Handle: *)

  h := v.handles[2];
  v[h] := 4343;

  h := v.find(2);
  IF ASSIGNED(h) THEN
    v[h] := 323;
  END;


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 19:59 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 473
О "шаблонной магии". Полиморфная параметризация модулей в стиле A2 сохраняет однородность модульности по-паскалевски, гораздо меньше "срывает крышу". Параметризация именно типов ("по месту") в общем случае может заставить, как минимум, ввести в язык вложенные типы. Да и "протаскивание" параметров по коду отнюдь нелёгкое занятие, мороки хватает. К тому же при наличии локальной параметризации всё равно тяжело без "ассоциированных типов", например:
https://rurust.github.io/rust-by-example-ru/generics/assoc_items/types.html

Однако, родовые модули имеют некоторую ограниченность в возможностях. Частично (собственно то, не мало) ограниченность снимается с помощью тех же тегов типа.
Ключевое -- параметры-типы модуля есть те же метатипы. А также для удовлетворения и "мета-runtime" необходима возможность "инстанцирования" модуля не только в IMPORT (в разрезе всего модуля), но и по месту.

Реализация полиморфного "вектора":
Код:
MODULE Containers(TYPE T);

TYPE
  Handle = ...;

  Vector = RECORD
    buf: ARRAY OF T;
    ...
  END;

  (* инициализатор/конструктор как функция *)
  PROCEDURE &Init(capacity: INTEGER = 10): Vector;
  BEGIN
    ( * применяется расширенная форма оператора RETURN из Ада -- см. далее * )
    RETURN v: Vector DO
      (* как-то используем capacity и прочие настройки *)
      v.capacity := capacity;
      ...
    END;
  END;

  (* поиск. Здесь могут быть задействованы некие операции аля "<" для типа T *)
  PROCEDURE find(v: Vector; a: T): Handle;
  BEGIN
    ...
  END;

  (* Поиск по элементу внутри структуры в предположении, что тип T является структурным.
     Параметр U определён как входящий в T, он может быть любой вложенности в иерархии RECORD-ов
     или где-то внутри массивов из "TYPE OF" в зависимости от фактического определения типа T.

     Тип ANYTYPE -- синоним для "TYPE OF ANY" -- предельно универсальный базовый метатип.

     Тип для "a" указан как "зависимый тип", т.е. в данном случае тип U введён локально.
     Если бы была локальная полиморфная параметризация, то процедура имела бы примерно такой вид:
     PROCEDURE findBy[TYPE T; TYPE U IN T](v: Vector(T); a: U): Handle;
  *)   
  PROCEDURE findBy(v: Vector; U: ANYTYPE IN T; a: U): Handle;
  VAR
    e: T;
    h: Handle;
  BEGIN
    ...
    (* используем переменную U как полноценный метатип *)
    e := v.buf[...];
    IF U[e] = a THEN
    ...
    END;

    (* Или альтернативный вариант алгоритма.
       Глобальный параметр-тип модуля T также является метатипом *)
    h := buf.handles[...];
    e := T[h];
    IF U[h] = a THEN
    ...
    END;
  END;

  (* Перегрузка оператора "индекс" (чтение в данном случае) для метатипа,
     т.е. для возможности применять так:
     VAR v: Containers(INTEGER).Vector;
         h: Handle;
         t: TYPE OF INTEGER;
         i: INTEGER;
     ...
     h := v.find(100);
     i := t[h]; (* вызов PROCEDURE "[]" ...*)
  *)   
  PROCEDURE "[]"(tt: TYPE OF T; h: Handle): T;
  BEGIN
    (* Для простоты пусть тип Handle есть некий указатель
       на тип T (тип элементов вектора). Здесь компилятор "разрулит"
       оператор "индекс" уже по типу T, в том числе если фактический "TYPE OF T"
       является тегом на вложенные элементы в структуре  *)
    RETURN tt[h^];
  END;


(* применяем тип: *)

MODULE MyModule;
IMPORT CR := Containers(Rec);  (* глобальное инстанцирование *)
...
TYPE
  Rec = RECORD
    a, b: INTEGER
  END;
...
VAR
  v: CR.Vector;
  h: Handle;
...
BEGIN
  (* инициализация с вызовом &Init(capacity)  *)
  v := NEW(100);
 
  (* поиск по полю "a" *)
  h := v.findBy(Rec.a, 23);
...

(* применяем тип с использованием метатипов в runtime *)

MODULE MyModule;
IMPORT C := Containers;  (* нет глобального инстанцирования *)
...
VAR
  v: C.Vector;
  ti: TYPE OF INTEGER;
  tr: TYPE OF REAL;
  h: Handle;
...
BEGIN
  (* Инициализируем с "runtime-инстанцированием", в т.ч. есть вызов &Init(capacity)
     как при использовании NEW  *)
  v := C([ti, tr]).Vector(100);
  ...
  h := v.find([23, 4.6]); (* поиск по "полному" значению *)
  h := v.findBy(ti, 30);  (* поиск по "полю" ti *)


(* применяем тип с использованием метатипов в compiletime *)

MODULE MyModule;
IMPORT C := Containers;
...
VAR
  h: Handle;
  ti: TYPE OF INTEGER;
  tr: TYPE OF REAL;

  (* статическое инстанцирование и инициализация по месту *)
  v: C([ti, tr]).Vector := NEW(100);
...
BEGIN
  ...
  h := v.find([23, 4.6]); (* поиск по "полному" значению *)
  h := v.findBy(ti, 30);  (* поиск по "полю" ti *)


Таким образом, сохраняется однородность между полиморфными параметрами и метатипами. Гипотетически компилятор может осуществлять все необходимые оптимизации, в т.ч. и inline (регулируемо). Для параметра типа вида динамического массива из тегов "TYPE OF" (статически не известного типа в полном объёме) возникает вариант "runtime-инстанцирования", с соответствующими последующими runtime-операциями доступа к данным и т.д.

В общем случае определение полиморфного (и не только) типа является распределенным. Например, в ином модуле можем задать новый инициализатор:
Код:
MODULE ExContainers(TYPE T);
IMPORT C:= Containers(T);

...
  PROCEDURE &FromArray(a: ARRAY OF T): C.Vector;
  BEGIN
    (* предварительно применяем Init из C *)
    RETURN v: C.Vector := C.Init() DO
      ...
    END;
  END;

Применять понятие "методов" с неявным SELF, располагая процедуры внутри "тела" типа, неудобно (в широком масштабе). Пусть все методы есть обычные процедуры (первый параметр может быть интерпретирован как "ресивер" для "объектной" формы вызова вида "o.met()"). Конструкторы являются функциями, возвращающими объект целевого типа. В данном случае необходимо предполагать нечто вроде паскалевской переменной Result или мол имя функции как результат (реальный объект уже предварительно создан). Но удобнее применить (для конструкторов -- обязать) аналог расширенной формы оператора return из Ада (RETURN ... DO ... END), семантика которого предполагает отсутствие копирования при "возврате" и пр. (для тамошних "лимитированных" типов, кстати, полезных с запретом копирования).
Стиль инициализаторов типа предполагается как в A2, в т.ч. операция NEW (или runtime "обращение к типу" при инициализации) осуществляет подбор нужной процедуры-конструктора, как для указателей, так и для типов-значений (по аналогии с "математическими" массивами).
Для параметров процедур, особенно полиморфного типа, полезен стиль из "Оберон 3" -- для структурных типов "IN"-модификатор (передача по ссылке) подразумевается по умолчанию.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 20:05 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 473
О "pattern matching". При наличии в языке константных выражений для массивов вида "[1, 2, 3]" вполне допустимо обобщение понятий массива и структур (массив есть структура с элементами одинакового типа). Тогда можем указывать значения в виде:
Код:
VAR r: RECORD a: INTEGER; b: REAL END := [a: 10, b: 345.5];
(* или *)
VAR r: RECORD a: INTEGER; b: REAL END := [10, 345.5];

С помощью WITH можем разложить объект на части:
Код:
VAR
  o, x, xs, y, z: ANY;
...
  WITH o:
  | [] DO ...(* пусто или тип UNIT *)
  | [x, y: INTEGER, z: REAL] DO ...(* объект "o" из 3-частей, переменные x, y, z теперь соотв. типа *)
  | [x: INTEGER, ..xs] DO some_proc_int(x)  (* переменная xs теперь указывает на оставшийся "хвост" объекта *)
  | [x: REAL, ..xs]    DO some_proc_real(x)
  ELSE
    ...
  END;

Раскрывать "variadic templates" можем так:
Код:
MODULE Str(TYPE T);

(* Универсальная операция для представления объекта в виде строки.
   Процедура может быть переопределена, имеет реализацию *)
EXTENSIBLE PROCEDURE str*(a: T): STRING;
BEGIN
  ...
END;
...

MODULE Repr(TYPE T);
IMPORT Str(T);

(* новый вариант реализации str *)
NEW PROCEDURE str*(a: T): STRING;
BEGIN
  ...
  s := INHERITED(a); (* вызов исходного варианта операции *)
  ...
END;

(* Универсальное "репрезентативное" (с форматированием для "печати") представление объекта
   в виде строки, с вариантом реализации по умолчанию *)
EXTENSIBLE PROCEDURE reprShow*(a: T): STRING;
  (* вложенная процедура, только объявление сигнатуры *)
  PROCEDURE make_str(sb: StrBuilder; a: T);
  (* вариант перегрузки относительно параметра полиморфного типа,
     в данном случае объект "a" не пустой, хвост "xs" всегда не определён -- только "ссылка" на
     последующую часть объекта, ссылка может оказаться "пустой" *)
  PROCEDURE make_str(sb: StrBuilder; a: [x: ANYTYPE, ..xs: ANYTYPE]);
  BEGIN
    (* через "x[a]" получаем значение части "a" *)
    sb.append(str(x[a]));  (* пусть будет примитивная реализация алгоритма *)
    make_str(sb, xs[a]);   (* рекурсивный вызов для "хвоста" *)
  END;
  (* конец рекурсии *)
  PROCEDURE make_str(sb: StrBuilder; []); BEGIN END;

VAR
  sb: StrBuilder := NEW(...);
BEGIN
  make_str(sb, a);
  RETURN sb.toString();
END;

(* применяем операцию: *)

MODULE MyModule;
IMPORT r := Repr WITH (show := reprShow); (* импортируем с переименованием *)

VAR
  s: STRING;
  i: INTEGER;
  r: SomeRec;
  ta: TYPE OF INTEGER;
  tb: TYPE OF REAL;
...
  s := r(INTEGER).show(i);
  s := r(SomeRec).show(r);
  s := r([ta, tb]).show([ta(1234), tb(234.434)]);

Для make_str вместо рекурсии (с хвостовой оптимизацией) в случае "статического" инстанцирования компилятор может выполнить inline каждого вызова (регулируемо).

"Runtime-инстанцирование" при "динамическом" типе-параметре возникает так:
Код:
...
VAR
  arr: ARRAY of ANYTYPE;
  t: ANYTYPE;
  data: ANY;
  v: C.Vector;
  ...

  (* как-то наполняем массив arr *) 
  WHILE ... DO
    data := ...;
    WITH data:
    | INTEGER DO
        t := INT64;       

        (* пусть для дин. массивов определен "+" как при работе со строками,
           массив сам расширяется *)
        arr := arr + t;
    | REAL DO
        arr := arr + REAL;
    ...
    END;
  END;
 
  (* инициализируем вектор с заданной структурой элементов *)
  v := C(arr).Vector;
  ...

При операции "разложение" типа доступны только "публичные" части. Компоненты типов, "слепленные" из метатипов, считаются общедоступными.
При тестировании типа возможно уточнение вида "IN <type>" и "OUT <type>" для определения возможности изменения данных.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 20:12 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 473
И самый тяжёлый вынос мозга. Чтобы полностью удовлетворить потребность в "умных макросах", но на основе системы типизации, необходимы "функторы над модулями" аля как в ML. Точнее, функции над типами, в предположении, что возможны некие compiletime-вычисления (а то и любые, вплоть "с побочными эффектами", т.е. с выходом во внешнюю среду):
Код:
(* Пусть есть некий шаблонный модуль *)
MODULE M(CONST Size: INTEGER; TYPE K, V);

TYPE
  Rec = RECORD
    Key: K;
    Value: ARRAY[Size] OF V;
  END;
...

(* используем шаблон *)
MODULE MyModule;
IMPORT M(SizeData, Keys, Vals);

(* Фукнция типа -- для выходного результата допустимы метатипы и типы, пригодные
   в качестве типов для значений констант *)
TYPE PROCEDURE makeTypes(S: INTEGER = 100; K: TYPE OF Numeric = INTEGER): (INTEGER, TYPE OF Numeric, ANYTYPE);
BEGIN
  RETURN ds: INTEGER := S * 10, tk: TYPE OF Numeric, tv: ARRAY OF (TYPE OF REAL) DO
    if K(:INTEGER) THEN tk := INT64
    ELSE tk := K;

    (* тип для tv указан как дин. массив метатипов. Конечный результат интерпретируется как структура, напр.:
       tv := NEW(3)   => RECORD REAL, REAL, REAL END *)
    tv := NEW(ds + 1);
  END;
END;

(* Определяем константу и типы *)
CONST SizeData, TYPE Keys, TYPE Vals = makeTypes(100, INT64);

(* ... и используем: *)
Var
  r: M.Rec;
  arr: ARRAY[SizeData] OF Vals;
...

Функции типов также играют роль мета-конструктора типа:
Код:
(* универсальный модуль с "STL"-контейнерами, имеющий один параметр (тип элемента контейнера) *)
MODULE Containers(TYPE T);

TYPE
  (* Для типа вида Map необходимо выделить две части из T -- "ключ" и "значение": *)
  Map = RECORD
    (* константы как метатипы *)
    CONST K: ANYTYPE IN T;
    CONST V: ANYTYPE IN T;

    (* поля структуры ... *)
    ...
  END;

(* Мета-конструктор (с "&", как и runtime-конструкторы) типа *)
TYPE PROCEDURE &Init(tk, tv: ANYTYPE IN T): Map;
BEGIN
  RETURN m: Map DO
    m.K := tk;
    m.V := tv;
  END;
END;
...

(* Используем Map *)

MODULE MyModule;
IMPORT C := Containers;
...
TYPE
  Rec = RECORD
    k: TYPE OF INTEGER;
    v: TYPE OF STRING;
  END;

VAR
  m: C(Rec).Map(Rec.k, Rec.v); (* объявление типа с вызовом мета-конструктора *)
...

(* в варианте с использованием тегов типа *)
VAR
  k: TYPE OF INTEGER;
  v: TYPE OF STRING;
  m: C([k, v]).Map(k, v);
... 


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 20:14 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 473
Таким образом, понятие метатипа, фактически, стирает границы между compiletime и runtime, облегчая задачи и для возможного "интерактивного" devtime, давая почву для разговора и в сфере потенциального аля "SPARK", максимальная однородность технологического языка (как путь уменьшения меры неоднородности).

Вопрос в том, насколько такие мета-навороты ("классы типов" или "ML-модули" на коленке по-паскалевски/обероновски (однако, в данном случае, фактически, есть и частичный эквивалент средств вида "template haskell" и т.п.)) разрывают мозг по сравнению с "ручным" (и разным) перелопачиванием runtime-рефлексии и неких потрохов компилятора.

P.S. Сорри за большой поток. Буду признателен, если есть критические замечания...


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 21:00 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
PSV100 писал(а):
необходимо распределять "права доступа" к возможностям, может быть как раз через упомянутые выше RESTRICT.

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

В том же Дельфи уже есть ограничение - в сериализацию попадают только published свойства.

Вот, собственно, на этом и можно было бы ограничиться при обсуждении всей данной темы. Но нет, обязательно нужно было раздуть :)


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 22 Ноябрь, 2019 21:09 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1557
PSV100 писал(а):
Имхо, первичный критерий верно отмечен -- для прикладного программирования рефлексия не должна нарушать модульность.

Это если модульность ещё есть. Насколько я понял, в оберонах есть более-менее общая дыра, хотя я ещё её не проверял, но некоторые писали, что она действительно работает. А работает она так:

* загружаем модуль А с типом-записью Аа. В нём хранится список указателей на Аа в глоб.перем Сп
* В модуле Б есть тип-потомок Аб типа-записи Аа, с переопределёнными методами
* Кладём экземпляр Аб в Сп
* Выгружаем модуль Б.

Два вопроса:
* Сможем ли мы выгрузить модуль Б?
* Что случится при вызове переопределённого метода от Аб?

Если такая дыра есть, то её причиной является наследование через границу модулей, которое нарушает модульность уже само по себе. Даже если её нет, проблема того, что "наследование нарушает инкапсуляцию", давно известно, а инкапсуляция - это свойство модульности. Так что если не в точном смысле, то во всяком случае близка к тому, что с модульностью в Обероне и так не слишком хорошо. Тут как бы можно задать резонный вопрос - а есть ли нам ещё, что беречь, или у нас уже всё равно нет модульности?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Суббота, 23 Ноябрь, 2019 00:19 

Зарегистрирован: Четверг, 08 Май, 2008 19:13
Сообщения: 1447
Откуда: Киев
Илья Ермаков писал(а):
Я считаю, что для показанного нужен "режим интерпретации": например, когда считанная из формы-как-документа строка используется для доступа к нужной переменной и т.п. Это невозможно сделать до времени выполнения.
Если переменная подразумевается для связывания с документом, то её или, возможно, то что её будет заменять можно зарегистрировать для связывания. У данных есть явный или неявный инвариант и просто отдавать на откуп некой внешней сущности менять её - это плохая идея, даже если в конкретном частном случае этот инвариант - это любое значение.

Вот я смотрю на Menus. Что мешает команду StdCmds.Undo зарегистрировать как объект, который можно вызывать извне?

Кстати, почему Guard записывается отдельно, а не является частью единой сущности с командой? Хочу пишу, хочу не пишу или напишу от другой команды?
Также, случайно заметил, что HostCmds.PasteObjectGuard в GNU/Linux версии закоментирован, но в меню упоминается и при вызове пункта меню выдаётся сообщение Item guard HostCmds.PasteObjectGuard for HostCmds.PasteObject not found


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Суббота, 23 Ноябрь, 2019 01:31 

Зарегистрирован: Четверг, 08 Май, 2008 19:13
Сообщения: 1447
Откуда: Киев
budden писал(а):
1. чем конкретно плоха автоматическая сериализация/десериализация
на базе рефлексии для специальным образом помеченных (или специальным образом не помеченных) полей структур данных? Какая именно абстракция при этом протекает?
Программирование - это во многом создание языка предметной области и при грамотной декомпозиции сущность является не просто хранилищем данных, а предоставляет некий интерфейс и хранит в себе некоторые инварианты. Рефлексия - внешний по отношению к сущности инструмент, отдавать которому внутренние данные нежелательно.

Цитата:
Как рукописный код позволит этого избежать?
Рукописный код - это и есть воплощение языка предметной области.

Цитата:
2. чем конкретно плох механизм вызова команд через рефлексию, при том, что команда определяется, допустим, по тому, что первым параметром является commands.Context? Какая абстракция при этом протекает и как рукописный код позволит этого избежать?
Вопрос звучит как-будто о том, что будет, если имея такой разносторонний инструмент, которым можно наворотить, его буду использовать строго корректно. Тем, кто пишет без ошибок опасаться нечего. Речь, естественно, идёт не о том, что всегда и везде будут моментальные последствия, тогда и говорить было бы не о чем, а о стратегии защиты от ошибок, подразумевающей использование более простых, ограниченных средств вместо более навороченных.

А будь всё так, эволюция быстро бы сделало своё дело:
Изображение


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 94 ]  На страницу Пред.  1, 2, 3, 4, 5  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2024, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB