Oberon space
General Category => Общий раздел => Тема начата: vlad от Ноябрь 13, 2013, 03:26:16 am
-
Буду пробовать добавить методы в текущую реализацию. Предлагаю пообсуждать - кто в каком виде их хочет видеть.
Примерные вопросы:
- виртуальность (всегда или могут быть невиртуальные методы)
- абстрактные методы (можно попробовать без них)
- интерфейсы (возможно стоит отделить чисто методы от чисто данных)
- синтаксис (где объявлять - в TYPE или потом вместе с остальными процедурами)
За основу можно взять проверенное решение из ББ.
-
За основу можно взять проверенное решение из ББ.
ProcedureHeading = PROCEDURE [Receiver] IdentDef [FormalParameters] MethAttributes.
Receiver = "(" [VAR | IN] ident ":" ident ")".
MethAttributes = ["," NEW] ["," (ABSTRACT | EMPTY | EXTENSIBLE)].
Интересен тут Receiver, который может быть как POINTER так и ссылкой на переменную. Это, как я понимаю, дает возможность "запретить" вызов каких-то методов для объектов, не полученных через NEW.
С другой стороны мне не нравится явная спецификация this/self. Оно конечно показательно при обучении, но вот мусора (синтаксического) и беспорядка (кто как хочет так и называет) больше. Т.е., в этом вопросе я однозначно за дополнительное ключевое слово this/self. Хотя в моем синтаксически любимом питоне self тоже явный :)
-
Да, решаемая задача формулируется так: подменить реализацию в случаях, когда подменить модуль (основное средство сокрытия реализации в Обероне) невозможно/неудобно. Поэтому любые альтернативные предложения, включая, например простую подмену модулей (не могу представить как это может выглядеть, но вдруг) - также принимаются.
Предлагаемый Виртом вариант инсталляции процедур в поля рекордов на общее/надежное/простое решение никак не тянет.
-
Ну и первый набросок:
TYPE
Interface1* = INTERFACE
method1();
method2(i: INTEGER): CHAR;
END;
PInterface1* = POINTER TO Interface1;
Interface2* = INTERFACE(Interface1)
method3();
END;
Interface3* = INTERFACE
method4();
END;
R1 = RECORD(Interface1)
i: INTGER
END;
R2 = RECORD(R1, Interface3)
c: CHAR
END;
PROCEDURE R1.method1();
BEGIN
this.i := 123;
END R1.method1;
PROCEDURE R2.method1();
BEGIN
this.method1^();
this.c := "a";
END R2.method1;
PROCEDURE make1*(): PInterface1;
VAR
result: POINTER TO R1;
BEGIN
NEW(result);
RETURN result
END make1;
PROCEDURE make2*(): PInterface1;
VAR
result: POINTER TO R2;
BEGIN
NEW(result);
RETURN result
END make1;
- интерфейсы можно наследовать (множественно)
- записи наследуют интерфейсы (множественно)
- интерфейс нельзя создать ни через NEW ни как переменную
- правила экспорта, приваивания, приведения типа - можно обсудить (вроде ничего сильно спорного пока не возникает).
Добавились ключевые слова INTERFACE и this. Но зато не нужен (вроде) весь арсенал из ББ - ABSTRACT, NEW.
-
Вапще я часто встречаю мнение, что интерфейсов достаточно, так что процедуры, привязанные к записям (как в обероне-2 и кп) нинужны.
В принципе, интерфейсы имеют сходство с классами типов хаскелла, так что я за них.
-
Мне кажется, что нужно отталкиваться от конкретных задач.
Т.е. добавляемая фича должна быть решением конкретной проблемы. Иначе фигня получится.
ps Сейчас натягиваю rbtree на CP. Подводные камни в самых неожиданных местах всплывают.
-
Немного разверну мысль:
имхо
Чтобы получить качественный результат нужно:
1. Сформулировать задачу
2. Попытаться решить задачу на O13 (разными путями)
3. Если решить не удалось или решение неудовлетворительное, то определить причину.
4. Если причина ясна, то предложить фичу
5. Если фича действительно дает профит при решении данной задачи/класса задач, то можно подумать о включении ее в язык.
-
Немного разверну мысль:
имхо
Чтобы получить качественный результат нужно:
1. Сформулировать задачу
2. Попытаться решить задачу на O13 (разными путями)
3. Если решить не удалось или решение неудовлетворительное, то определить причину.
4. Если причина ясна, то предложить фичу
5. Если фича действительно дает профит при решении данной задачи/класса задач, то можно подумать о включении ее в язык.
Причем не стоит забывать и о внеязыковых методах решения проблемы.
Пример - ключевые слова КАПСОм в языке набирать не удобно (мне по крайней мере) пальцы уставать начинают. Можно поменять язык, а можно написать плагин к текстовому редактору, который это будет делать за тебя. Что я и сделал. Теперь КАПС в языке проблем не вызывает.
-
Не совсем догоняю, а с ключевым словом ИНТЕРФЕС и звездочки не нужны будут?
Пока ничего определенного, по этому высказать не могу, кроме того, что было бы удобнее чем без этого даже как в КП (или именно так). Наследование интерфейсов будет только из текущего модуля, или других тоже?
Вроде удобство, но с другой стороны, чем то мне это напоминает типизацию/без типизации, только на другом уровне. Поэтому я пока за одиночное наследование (или как оно там называется)
-
А все расширизмы выльются в одну опцию компилятора, или в несколько (ну по крайней мере как задумывается :) )?
-
Мне кажется, что нужно отталкиваться от конкретных задач.
Т.е. добавляемая фича должна быть решением конкретной проблемы. Иначе фигня получится.
Безусловно. Задача сформулирована здесь: http://oberspace.dyndns.org/index.php/topic,579.msg19580.html#msg19580
Еще более конкретно - думаю как на обероне будет выглядеть вот это: https://github.com/vladfolts/oberonjs/blob/master/src/type.js
ps Сейчас натягиваю rbtree на CP. Подводные камни в самых неожиданных местах всплывают.
Дык, поделись ;)
-
2. Попытаться решить задачу на O13 (разными путями)
В данном случае решение на О7/13 (подстановка процедурных переменных) хорошо проработано (и описано самим Виртом). И оно неудовлетворительно. Могу расписать все недостатки, но они очевидны для любого, кто эмулировал это на О7 и может сравнить с тем, как это делается в языке с непосредственной поддержкой ООП.
3. Если решить не удалось или решение неудовлетворительное, то определить причину.
Причина тоже очевидна - нехватка выразительных средств языка.
4. Если причина ясна, то предложить фичу
5. Если фича действительно дает профит при решении данной задачи/класса задач, то можно подумать о включении ее в язык.
Фича предложена, давайте подумаем ;)
-
Пример - ключевые слова КАПСОм в языке набирать не удобно (мне по крайней мере) пальцы уставать начинают. Можно поменять язык, а можно написать плагин к текстовому редактору, который это будет делать за тебя. Что я и сделал. Теперь КАПС в языке проблем не вызывает.
На самом деле вызывает :) Потому что буков все равно много. Просто хотя бы набирать проще. Какое внеязыковое средство ты предлагаешь для этой проблемы?
-
Пример - ключевые слова КАПСОм в языке набирать не удобно (мне по крайней мере) пальцы уставать начинают. Можно поменять язык, а можно написать плагин к текстовому редактору, который это будет делать за тебя. Что я и сделал. Теперь КАПС в языке проблем не вызывает.
На самом деле вызывает :) Потому что буков все равно много. Просто хотя бы набирать проще. Какое внеязыковое средство ты предлагаешь для этой проблемы?
Автокомплит конечно! :-) Алсо число букв не зависит от того капс это или не капс :-)
-
Не совсем догоняю, а с ключевым словом ИНТЕРФЕС и звездочки не нужны будут?
Да, наверное все как с RECORD. Хотя кажется, что в основном интерфейсы будут использоваться как экспортируемыя сущность. Возможно надо сделать звездочку только на интерфейс (все методы экспортируются).
Пока ничего определенного, по этому высказать не могу, кроме того, что было бы удобнее чем без этого даже как в КП (или именно так). Наследование интерфейсов будет только из текущего модуля, или других тоже?
Да, конечно. Это основная идея. Реализовать экспортированный интерфейс может кто угодно. Расширяем систему не только подменой модулей.
Вроде удобство, но с другой стороны, чем то мне это напоминает типизацию/без типизации, только на другом уровне. Поэтому я пока за одиночное наследование (или как оно там называется)
Про множественное наследование уже несколько раз обсуждали. Не вижу смысла запрещать его конкретно для интерфейсов.
-
А все расширизмы выльются в одну опцию компилятора, или в несколько (ну по крайней мере как задумывается :) )?
Одна опция. Пока нет причин делать тонкий тюнинг фич.
-
Очень советую посмотреть как это все сделано в двух языках:
1) Go
2) Modula-3
3) Haskell
Ибо смотреть только со стороны Симула->С++->Java как-то однобоко :-)
-
Причем не стоит забывать и о внеязыковых методах решения проблемы.
Не помню рассказывал я или нет... В какой-то момент я пробовал писать на макроассемблере. Соответственно, там были макросы. И можно было писать:
do_something1();
do_something2();
...
Была видимость, что это почти ЯВУ. Главное макросы нужные написать. Проблема была в том, что это была только видимость :) Потому что все интересное (во всех смыслах) было внутри макросов. И оно было по-прежнему на ассемблере.
Так вот. Я предлагаю расширять язык, потому что я хочу писать/читать на языке, хорошо поддерживающем хорошо зарекомендовавшие себя подходы к решению задач. А не иметь видимость удобного языка силами IDE/визардов и т.п.
-
Очень советую посмотреть как это все сделано в двух языках:
1) Go
2) Modula-3
3) Haskell
Как это сделано в Го - ты рассказывал. И я помню идею о том, что любой может подложиться под интерфейс, если у него есть нужные методы.
Может ты и про модулу расскажешь? Или прямо тщательно надо изучить?
В хаскеле классы типов - Geniepro отдобрил :)
-
Причем не стоит забывать и о внеязыковых методах решения проблемы.
Не помню рассказывал я или нет... В какой-то момент я пробовал писать на макроассемблере. Соответственно, там были макросы. И можно было писать:
do_something1();
do_something2();
...
Была видимость, что это почти ЯВУ. Главное макросы нужные написать. Проблема была в том, что это была только видимость :) Потому что все интересное (во всех смыслах) было внутри макросов. И оно было по-прежнему на ассемблере.
Так вот. Я предлагаю расширять язык, потому что я хочу писать/читать на языке, хорошо поддерживающем хорошо зарекомендовавшие себя подходы к решению задач. А не иметь видимость удобного языка силами IDE/визардов и т.п.
Аналогия не катит - если бы я предлагал писать макросы для IDE на каждый чих - то было бы засчитано, а так нет :-)
Нужно выявить что именно неудобно и почему и подумать как это дело закткнуть. См. ту же Модулу - там язык не оторван от инструментария и спокойно продолжается в системе сборки например. К слову дженерики там частично реализованы именно в системе сборки. И это чертовски удобно! Очень хорошо когда система сборки знает что-то про язык.
-
Нужно выявить что именно неудобно и почему и подумать как это дело закткнуть. См. ту же Модулу - там язык не оторван от инструментария и спокойно продолжается в системе сборки например. К слову дженерики там частично реализованы именно в системе сборки. И это чертовски удобно! Очень хорошо когда система сборки знает что-то про язык.
Допустим. Есть конкретная проблема. Я предложил расширить язык. Как это сделать без расширения языка (посредством системы сборки или не знаю чего еще) я не могу придумать (в отличие от дженериков). У тебя есть идеи?
Надо понимать, что расширение языка из этого топика, даже будучи реализовано, не останется высечено в камне (потому что будет написан мильен строк продакшн кода с использованием этого расширения). Оно будет переосмысленно и улучшено (возможно до неузнаваемости) в процессе. Но с чего-то надо начинать.
-
Нужно выявить что именно неудобно и почему и подумать как это дело закткнуть. См. ту же Модулу - там язык не оторван от инструментария и спокойно продолжается в системе сборки например. К слову дженерики там частично реализованы именно в системе сборки. И это чертовски удобно! Очень хорошо когда система сборки знает что-то про язык.
Допустим. Есть конкретная проблема. Я предложил расширить язык. Как это сделать без расширения языка (посредством системы сборки или не знаю чего еще) я не могу придумать (в отличие от дженериков). У тебя есть идеи?
Это тот Type.js переписать шоле?
-
Ибо смотреть только со стороны Симула->С++->Java как-то однобоко :-)
Глянул модулу по диагонали. Ну не нравится мне смешивание данных (полей объекта) и контракта (методов).
Кроме того, из общих соображений: хочется иметь максимально чистую декларативную часть. Интерфейсы, как отдельная сущность, ее обеспечивают.
-
Это тот Type.js переписать шоле?
Да.
-
Это тот Type.js переписать шоле?
Только не надо меня обвинять в попытке натянуть чуждую парадигму (ООП) на оберон (процедурный). Типа если бы изначально компилятор писался в процедурной парадигме оберона - то и потребности в расширении языка не возникло бы. Я видел как оно написано у Вирта. Хреново написано (я в джаббер конфе делился конкретными кусками). И оно так хреново написано как раз потому, что так проще. А проще так в силу конкретных ограничений языка - простые и выразительные вещи в рамках ООП выражаются сложно и непонятно на обероне - и поэтому отметаются.
-
Это тот Type.js переписать шоле?
Только не надо меня обвинять в попытке натянуть чуждую парадигму (ООП) на оберон (процедурный). Типа если бы изначально компилятор писался в процедурной парадигме оберона - то и потребности в расширении языка не возникло бы. Я видел как оно написано у Вирта. Хреново написано (я в джаббер конфе делился конкретными кусками). И оно так хреново написано как раз потому, что так проще. А проще так в силу конкретных ограничений языка - простые и выразительные вещи в рамках ООП выражаются сложно и непонятно на обероне - и поэтому отметаются.
Я правильно понимаю что тебе нужна некая дополнительная гибкость? Можешь рассказать для чего именно и какая именно гибкость нужна конкретно вот в компиляторе? Ну, то есть под какого рода изменчивость код нужно приспособить, и зачем.
-
Я правильно понимаю что тебе нужна некая дополнительная гибкость?
Да. Мне нужно много реализаций одного и того же абстрактного интерфейса. Т.к., в обероне Вирт под абстрактным интерфейсом и сокрытием реализации подразумевает интерфейс модуля, то его подход не масштабируется на мой случай.
Можешь рассказать для чего именно и какая именно гибкость нужна конкретно вот в компиляторе? Ну, то есть под какого рода изменчивость код нужно приспособить, и зачем.
Есть сущность, отвечающая за представления Типа в компилтяторе. У типа есть описание (description), который разный для разных классов типов (чтоб не написать типов типов). Этот description замечательно ложится в ООП на метод расширяемого Type. Внезапно оказывается, что в обероне методов нет...
Вот ссылка на код еще раз (конкретнее некуда): https://github.com/vladfolts/oberonjs/blob/master/src/type.js
-
(http://cdn.imghack.se/images/a0ab59a4db362e684a79dbb73624eda0.jpg)
-
Вапще я часто встречаю мнение, что интерфейсов достаточно, так что процедуры, привязанные к записям (как в обероне-2 и кп) нинужны.
В принципе, интерфейсы имеют сходство с классами типов хаскелла, так что я за них.
Тут я немного погорячился. Интерфесы входят в иерархию наследования записи, а классы типов в этом плане больше похожи на наборы "extension method"-ов...
-
Вапще я часто встречаю мнение, что интерфейсов достаточно, так что процедуры, привязанные к записям (как в обероне-2 и кп) нинужны.
В принципе, интерфейсы имеют сходство с классами типов хаскелла, так что я за них.
Тут я немного погорячился. Интерфесы входят в иерархию наследования записи, а классы типов в этом плане больше похожи на наборы "extension method"-ов...
Ну да. классы типов обозначенную проблему никак не решают. Если к ним не прикручивать существовательные типы, кои есть богомерзкое расширение даже для Haskell'я. ;D
-
Вот ссылка на код еще раз (конкретнее некуда): https://github.com/vladfolts/oberonjs/blob/master/src/type.js
В коде я ничего не понял, но подозреваю, что для этой задачи методы не обязательны. Поскольку все типы известны заранее, достаточно расширения типа и CASE - IF-ELSIF. Это имеется ввиду под ужасом?
-
Вот ссылка на код еще раз (конкретнее некуда): https://github.com/vladfolts/oberonjs/blob/master/src/type.js
В коде я ничего не понял, но подозреваю, что для этой задачи методы не обязательны. Поскольку все типы известны заранее, достаточно расширения типа и CASE - IF-ELSIF. Это имеется ввиду под ужасом?
Насколько я понимаю, что с таким решением ровно одна проблема - при добавлении нового типа можно банально забыть CASE/IF-ELSE поправить и добавить еще один вариант. В случае же интерфейсов тут компилятор стукнет по голове - не реализован обязательный метод.
В принципе, в данном конкретном случае, задача могла бы решиться введением enum-типов с проверкой компилятором наличия всех возможных альтернатив в CASE.
-
В коде я ничего не понял, но подозреваю, что для этой задачи методы не обязательны. Поскольку все типы известны заранее, достаточно расширения типа и CASE - IF-ELSIF. Это имеется ввиду под ужасом?
Видишь ли, принципиальная проблема в IF/ELSIF в том, что я не помню все ли расширения типа представлены в этом модуле или есть какие-то еще за пределами. Да, и я не хочу искать - есть ли такие. А еще я хочу, чтобы все продолжало работать даже если какие-то расширения появятся в будущем. Может быть я хочу слишком большего... Но с другой стороны - что плохого в таком желании?
P.S. И да, я считаю IF/ELSIF по списку типов недостойным написания/чтения человеком. Ибо мусор, скрывающий суть. Даже если бы не было описанной выше принципиальной проблемы расширения системы.
-
Вот ссылка на код еще раз (конкретнее некуда): https://github.com/vladfolts/oberonjs/blob/master/src/type.js
В коде я ничего не понял, но подозреваю, что для этой задачи методы не обязательны. Поскольку все типы известны заранее, достаточно расширения типа и CASE - IF-ELSIF. Это имеется ввиду под ужасом?
Насколько я понимаю, что с таким решением ровно одна проблема - при добавлении нового типа можно банально забыть CASE/IF-ELSE поправить и добавить еще один вариант. В случае же интерфейсов тут компилятор стукнет по голове - не реализован обязательный метод.
В принципе, в данном конкретном случае, задача могла бы решиться введением enum-типов с проверкой компилятором наличия всех возможных альтернатив в CASE.
Вот как раз для этого и нужен HALT в CASE ;)
-
Согласен с предыдущим оратором. По моему опыт отсутствие нужной реализации даже без тестов отыскивается быстро.
-
Видишь ли, принципиальная проблема в IF/ELSIF в том, что я не помню все ли расширения типа представлены в этом модуле или есть какие-то еще за пределами. Да, и я не хочу искать - есть ли такие. А еще я хочу, чтобы все продолжало работать даже если какие-то расширения появятся в будущем. Может быть я хочу слишком большего... Но с другой стороны - что плохого в таком желании?
P.S. И да, я считаю IF/ELSIF по списку типов недостойным написания/чтения человеком. Ибо мусор, скрывающий суть. Даже если бы не было описанной выше принципиальной проблемы расширения системы.
Я как раз и говорил о случае, когда все типы известны заранее, соответственно ничего не потеряешь и помнить не надо - написал и дело с концом, как в случае с Обероном. Увы, зачастую только редкие и заранее предусмотренные динамические расширения не требуют дополнительного вмешательства в основной код. По поводу IF/ELSE тоже не стоит быть столь категоричным. Иногда общие части кода как раз и видны в таком виде, да в таких комбинациях, что с ООП там голову сломаешь. Если Вы заранее наметили пути расширения и видите четкий план развития, то вполне возможно интерфейсы и пригодятся в большей степени, чем это видится со стороны.
-
В принципе, в данном конкретном случае, задача могла бы решиться введением enum-типов с проверкой компилятором наличия всех возможных альтернатив в CASE.
Вот как раз для этого и нужен HALT в CASE ;)
Ненужен. valexey пишет об ошибке компиляции - я с ним согласен.
-
В принципе, в данном конкретном случае, задача могла бы решиться введением enum-типов с проверкой компилятором наличия всех возможных альтернатив в CASE.
Вот как раз для этого и нужен HALT в CASE ;)
Ненужен. valexey пишет об ошибке компиляции - я с ним согласен.
Я даже согласен на ошибку исполнения, но только такую которая будет вылетать сразу после загрузки модуля и до использования. Гарантированно будет выскакивать. Эдакий self test. Причем код этого self test'a не должен требовать изменения при добавлении новых типов. Такое вот решение вполне было бы приемлемо, я так делал несколько раз, когда у меня то ли мозгов нехватало на ошибку компиляции, то ли ограничения были какие-то. В результате прога, если допущена ошибка, просто отказывалась запускаться с четким указанием кто тут идиот и где ошибку искать.
А вот надеяться что при тестовых прогонах/запусках которые делает девелопер, будут проверены все ветки (в данном случае это означает, что там будут присутствовать все типы) - очень наивно. Неоднократно в продакшине сталкивались с ситациями когда хитрый пользователь-выдумщик такие входные данные дает, и в таких условиях запускает приложение, каких никогда не было у девелопера на прогонах, и там начинали играть всеми красками те самые, не оттестированные ветки кода.
-
Я как раз и говорил о случае, когда все типы известны заранее, соответственно ничего не потеряешь и помнить не надо - написал и дело с концом, как в случае с Обероном.
В таком случае лучше иметь CASE по енуму (с проверкой всех альтернатив на этапе компиляции), а не IF/ELSIF по произвольным типам...
Увы, зачастую только редкие и заранее предусмотренные динамические расширения не требуют дополнительного вмешательства в основной код. По поводу IF/ELSE тоже не стоит быть столь категоричным. Иногда общие части кода как раз и видны в таком виде, да в таких комбинациях, что с ООП там голову сломаешь. Если Вы заранее наметили пути расширения и видите четкий план развития, то вполне возможно интерфейсы и пригодятся в большей степени, чем это видится со стороны.
Я не хочу показаться одиозным адептом ООП. Я видел очень много применений механизмов ООП не там где надо (да че там, почти каждый день вижу). И, конечно, я не верю в беспроблемное расширение систем "если заранее все обдумать" (с этим к товарищу Усову). Я всего лишь хочу инструмент, позволяющий быстро и эффективно расширять систему с минимальными регрессионными ошибками и с возможностью легко избавляться от ставших неактуальными кусков. И интерфейс, как декларацию контракта с одной стороны и средство сокрытия реализации с другой - мне заменить нечем. Модули, как я говорил, решают эту же задачу, но статически и крупномасштабно. А очень часто нужна динамика и по месту.
-
Я конечно плохо понял суть разговора. Поэтому такой вопрос: паттерн DIR разве не решает проблему? В крайнем случае можно написать интерфейсный модуль я думаю даже, на основе нескольких других, используя dir. А его генерацию можно сделать внеязыковыми средствами (просто надо их продумать). Вон в 1С конструктор запросов очень удачно генерит тексты запросов, и с ним удобно работать. В данном случае думаю можно сделать что-либо подобное.
Мне например, на текущий момент вполне хватило бы следующей генерации:
Имеем:
TYPE
typ1 = RECORD
END;
-
Отправил раньше, чем надо. Заново теперь :
Я конечно плохо понял суть разговора. Поэтому такой вопрос: паттерн DIR разве не решает проблему? В крайнем случае можно написать интерфейсный модуль я думаю даже, на основе нескольких других, используя dir. А его генерацию можно сделать внеязыковыми средствами (просто надо их продумать). Вон в 1С конструктор запросов очень удачно генерит тексты запросов, и с ним удобно работать. В данном случае думаю можно сделать что-либо подобное. Тогда ошибки будут сыпаться по прежнему при компиляции
Мне например, на текущий момент вполне хватило бы следующей генерации (тут конечно просто блокнота мало будет, но кто ж их сейчас использует):
Имеем до генерации:
TYPE
typ1 = RECORD
END;
PROCEDURE typ1Proc1 ();
TYPE
typ1 = RECORD
Proc1: PROCEDURE ()
END;
PROCEDURE typ1Proc1 ();
Название типа, в процедуре для упрощения (а зачем собственно усложнять :) , к тому же вложенные процедуры все равно нельзя использовать )
Ну и где-нибудь в комментарии (где тип, или в секции БЕГИН, или еще где), добавить код присвоения (чтобы его потом просто скопировать). Или даже так, стоя на типе, указываем, что надо сгенерить присвоения.
Ну и что ж что больше кода будет. В 1С вон какие длинные идентификаторы для этого используют, и ничего :) . Потом можно будет говорить, что мильон строк накодил
-
Я конечно плохо понял суть разговора. Поэтому такой вопрос: паттерн DIR разве не решает проблему? В крайнем случае можно написать интерфейсный модуль я думаю даже, на основе нескольких других, используя dir. А его генерацию можно сделать внеязыковыми средствами (просто надо их продумать).
Не очень понимаю, что ты имеешь ввиду под интерфейсным модулем и как это поможет иметь одновременно (в рантайме) множество реализаций (в виде модулей?) нужного интерфейса.
-
В данном случае под интерфейсным модулем я имею в виду сгенеренный автоматом модуль, в котором будут реализованы нужные процедуры и чего там еще нужно из других модулей (просто как их вызов). Думаю так же думаю, и типы можно скомпилировать. Но уже на основе процедур данного модуля. Но для этого нужен какой-то удобный конструктор.
По сути это будет обычный модуль.
Про одновременно множество реализаций ничего сказать не могу.
А как директории динамически подменяются? Это не то, чего в итоге требуется? Зачем множество реализаций? Подменил директорию на нужную, и использовал. Более "внятно" ответить вряд ли смогу, т.к. плохо разбираюсь в вопросе.
-
Подменил директорию на нужную, и использовал.
Так. Давай по порядку. Что такое "подменил директорию"?
-
В принципе, в данном конкретном случае, задача могла бы решиться введением enum-типов с проверкой компилятором наличия всех возможных альтернатив в CASE.
Вот как раз для этого и нужен HALT в CASE ;)
Ненужен. valexey пишет об ошибке компиляции - я с ним согласен.
А чем конкретно в данном случае ошибка компиляции лучше?
HALT в CASE вылетает в 90 процентах случаев при первом же запуске. (проверено на практике неоднократно) Это на пару секунд позже компиляции. Эти пара секунд так важны?
-
А вот надеяться что при тестовых прогонах/запусках которые делает девелопер, будут проверены все ветки (в данном случае это означает, что там будут присутствовать все типы) - очень наивно. Неоднократно в продакшине сталкивались с ситациями когда хитрый пользователь-выдумщик такие входные данные дает, и в таких условиях запускает приложение, каких никогда не было у девелопера на прогонах, и там начинали играть всеми красками те самые, не оттестированные ветки кода.
Я раньше тоже так думал. До тех пор пока не начал пользоваться. На практике же ситуация другая. HALT вылетает сразу. Причина банальна. Когда мы добавляем новое сообщение, то 100% мы его тестируем, т.е. делаем хотя бы 1 прогон с этим новым сообщением.
Если разраб свой код вообще не запускает и вываливает в продакшин, то да... беда.
-
А чем конкретно в данном случае ошибка компиляции лучше?
Ошибка компиляции вообще лучше, а не только в данном случае :)
Предлагаю увеличить масштаб. У тебя мегапроект. Ты нашел все 100 мест (я не преувеличиваю - 100), где этот енум фигурирует в CASE и поправил эти места. Что дальше?
- Прогнать ты все эти места физически не можешь. Даже профессиональный QA не может. Автоматические тесты со 100% покрытием для старых мегапроектов (все мегапроекты - старые) - это из области космической фантастики.
- Таки одно место из этих 100 ты не поправил (мышка дрогнула).
- Ты искал только в мегапроекте, а есть еще несколько проектов поменьше (и тоже старых, их пересобирают раз в год и никто не хочет с ними иметь дело), которые тоже используют этот енум. На руках у тебя не вся codebase (например потому, что тебе не нужны все поддерживаемые платформы), поэтому все 200 мест ты не нашел. А если и нашел, то все равно ничего собирать, запускать и проверять не будешь.
Это все, конечно, можно пережить. Не Ариан-5 запускаем. Баги зарепортят и фикснут. Однако ошибки компиляции помогают сделать процесс внесения изменений менее страшным.
-
Я не спорю, что ошибка времени компиляции лучше. Лично мне не очень понятно, что планируется получить в итоге.
При изменении языка можно руководствоваться разными соображениями:
1. Джаст фо лулз.
2. Сделать качественно на чужом опыте.
3. Сделать качественно, но не так как у других.
Если 1 пункт, то и говорить не о чем :) (что хочешь, то и воротишь)
Если 2 пункт, то можно сделать 1:1 как в O2, к примеру. Тут тоже не о чем особо говорить (варианта всего 2... либо делать, либо нет)
А вот третий пункт требует сжигания калорий в большом количестве. Закон сохранения энергии никто не отменял. Качественный результат получается только при значительных затратах энергии.
Кто-то эту энергию должен потратить. Либо другой чувак (пункт 2), либо сам (пункт 3).
Энергию, конечно, можно разными путями тратить. Можно усиленно думать и методично добиваться результата, а можно ткнуть пальцем в небо и долго-долго потом наступать на грабли, модифицировать и отлаживать сие.
Предлагаю увеличить масштаб. У тебя мегапроект. Ты нашел все 100 мест (я не преувеличиваю - 100), где этот енум фигурирует в CASE и поправил эти места. Что дальше?
Мне очень сложно себе представить, чтобы у объекта было 100 методов.
И кроме того, даже эти сто методов, по идее, должны быть в одном модуле, если мы на Обероне пишем.
Я в общем то не против ООП. Даже всеми руками за. Вопрос лишь в том, как это сделать правильно. А это как, имхо, должно вырасти из весомых причин.
Можно сделать как в O2, но чем это будет отличаться от O2? ;)
-
Еще добавлю:
Если отталкиваться от реальных конкретных задач, то в данном твоем примере в компиляторе CASE вполне достаточно.
А рассуждения в контексте надуманных задач считаю мало полезным занятием :)
-
Так. Давай по порядку. Что такое "подменил директорию"?
Это лучше в документации к ББ посмотреть: в исходниках это обычно экспортируемая переменная Dir и StdDir . StdDir это реализация для Dir по умолчанию, Dir можно устанавливать при выполнении программы
Если я конечно это правильно понял
Конечно в данное случае при разработке придется придерживаться каких то соглаешний
-
Предлагаю увеличить масштаб. У тебя мегапроект.
Не уверен, что Оберон в принципе подходит как основа для мегапроекта. Он создавался для чего-то обозримого во многом в противовес этому.
Если метите в сверхзадачи, может лучше сразу взять за основу Аду? Или надеетесь, что сможете лишь слегка подрихтовать Оберон, и всё наладится? Не последуют ли за одними улучшениями другие, без которых мегапроект тоже не состоится?
-
А чем конкретно в данном случае ошибка компиляции лучше?
Всё, что может быть проверено во время компиляции -- должно проверяться во время компиляции.
Тесты -- работа для человека, а работать должен компьютер.
Компиляция тоже ведь своего рода тест на корректность программы, где-то слабый тест (в сях, например), где-то сильный (в системах доказательства теорем, например)...
-
Да я ж не спорю. Но в данном случае это не является проблемой. Метод решения просто странный.
- Есть маленькая вероятность что HALT не сработает....
- Давайте вкрутим ООП чтобы исключить эту маленькую вероятность!
Это все равно что на одного фашиста целую атомную бомбу сбрасывать...
-
А вот надеяться что при тестовых прогонах/запусках которые делает девелопер, будут проверены все ветки (в данном случае это означает, что там будут присутствовать все типы) - очень наивно. Неоднократно в продакшине сталкивались с ситациями когда хитрый пользователь-выдумщик такие входные данные дает, и в таких условиях запускает приложение, каких никогда не было у девелопера на прогонах, и там начинали играть всеми красками те самые, не оттестированные ветки кода.
Я раньше тоже так думал. До тех пор пока не начал пользоваться. На практике же ситуация другая. HALT вылетает сразу. Причина банальна. Когда мы добавляем новое сообщение, то 100% мы его тестируем, т.е. делаем хотя бы 1 прогон с этим новым сообщением.
Если разраб свой код вообще не запускает и вываливает в продакшин, то да... беда.
У компилятора реально очень много веток и комбинаций. Мы УЖЕ сталкивались с ситуацией когда был халт у компилятора при компиляции программ пользователя (можно поискать по форуму - оно тут есть). На тестах и прогонах это не вылезало. А все потому, что js как раз следует этой модели - все проверки в рантайме, а не на этапе компиляции. Динамическая типизация, ага. Причем то был халт не из за опечатки, а именно из за того что именно не все возможные случаи были рассмотрены.
-
А с ООП было бы по другому?
В общем не буду больше спорить. Но лично я пруфа не вижу.
ps Насчет динамической типизации... Все время слышу про проблемы с ней. Но сам не сталкиваюсь...
Несмотря на то, что написал далеко не один десяток тысяч строк кода на языке с динамической типизацией. Бывают, конечно, ошибки из-за нее, но слишком редко, чтобы считать это проблемой.
-
Ага, если еще учесть, что обновления от 1с типовых конф, которые тестируются (ну или должны, я надеюсь), пекутся как пирожки. Как минимум усложнение "писанины" повлияет на количество этого кода :) , ну и соответственно меньше будет исправлений
-
Да, по решаемой задаче, я полагаю, что и ООП тут так себе подходит. То есть это не тот случай когда число произвольных подтипов должно быть неизвестно, это не случай когда нужна произвольная неподконтрольная расширяемость.
Тут, как мне кажется, вполне подойдет явный список типов перечисленных в одном месте (как собственно по факту и имеем в коде).
Основываясь на этом и буду ваять решение. Вроде бы кое-что придумалось.
-
Не очень понимаю, что ты имеешь ввиду под интерфейсным модулем и как это поможет иметь одновременно (в рантайме) множество реализаций (в виде модулей?) нужного интерфейса.
У модуля, который содержит абстрактный интерфейс, делается экспортная переменная, в которую втыкается текущая реализация по-умолчанию (фабрика)
Переменную обычно называют Dir.
Также есть умолчательная фабрика StdDir.
BlackBox uses special objects with factory methods,
so-called factory objects. In BlackBox, factory objects are
used in a particular way: they are installed in global variables
and may be replaced at run-time, without affecting client code.
For historical reasons, we call factory objects which are used
for configuration purposes directory objects.
ps Ну это шаблон ООП ессесно.
-
Не очень понимаю, что ты имеешь ввиду под интерфейсным модулем и как это поможет иметь одновременно (в рантайме) множество реализаций (в виде модулей?) нужного интерфейса.
У модуля, который содержит абстрактный интерфейс, делается экспортная переменная, в которую втыкается текущая реализация по-умолчанию (фабрика)
Переменную обычно называют Dir.
Также есть умолчательная фабрика StdDir.
BlackBox uses special objects with factory methods,
so-called factory objects. In BlackBox, factory objects are
used in a particular way: they are installed in global variables
and may be replaced at run-time, without affecting client code.
For historical reasons, we call factory objects which are used
for configuration purposes directory objects.
ps Ну это шаблон ООП ессесно.
Это решение вообще никакого отношения не имеет к обсуждаемой задаче. Ну, то есть просто не в тему :-)
-
Я просто пояснил, о чем говорил adva.
-
BlackBox uses special objects with factory methods,
so-called factory objects. In BlackBox, factory objects are
used in a particular way: they are installed in global variables
and may be replaced at run-time, without affecting client code.
For historical reasons, we call factory objects which are used
for configuration purposes directory objects.
Я правильно понимаю, что втыкается как раз объект с методами. Т.е., даже не рекорд с процедурными полями?
P.S. Да, это из другой оперы.
-
Основываясь на этом и буду ваять решение. Вроде бы кое-что придумалось.
Спасибо всем, кто высказался. Подытожу мою точку зрения в одном месте.
Да, я хочу инструмент, который потянет мегапроекты. Причем не какие-то уникальные в своем роде, а тупо большие с большим количеством итераций. Потому что маленькие проекты можно делать на любом говне (oberonjs тому наглядное подтверждение).
Я верю, что еще есть куда копать для достижения данной цели - Ада не конечная точка для такого инструмента.
При прочих равных я всегда предпочту иметь общее/стандартное/масштабируемое решение, чем частное. Т.е., вместо того, чтобы из раза в раз выписывать IF/ELSIF - я лучше добавлю интерфейсы/методы в инструмент.
-
Я верю, что еще есть куда копать для достижения данной цели - Ада не конечная точка для такого инструмента.
Оберон как стартовая точка просто потому, что в нем нет ничего лишнего и нет фатальных ошибок в оригинальном дизайне. Можно спокойно экспериментировать. Иллюзий относительно того, что оригинальный оберон не подходит для мегапроектов, у меня никогда не было.
-
Я правильно понимаю, что втыкается как раз объект с методами. Т.е., даже не рекорд с процедурными полями?
Да
-
Полезные документики в тему:
The Evolution of Oberon-2 to Component Pascal (http://oberon2005.oberoncore.ru/paper/p_o2cp.pdf)
Metaprogramming in Oberon (http://oberon2005.oberoncore.ru/paper/eth10655.pdf)
Extensibility in the Oberon System (http://oberon2005.oberoncore.ru/paper/p_ext.pdf)
Object Oberon. An Object-Oriented Extension of Oberon (http://oberon2005.oberoncore.ru/paper/eth109.pdf)
Design and Implementation of a
Component Architecture for Oberon (http://oberon2005.oberoncore.ru/paper/eth11697.pdf)
см. "Overview of Design Concepts"
-
Вот кстати:
StyledTextDesc = RECORD (TextDesc)
fonts: FontList;
PROCEDURE (t: StyledText) SetFont (from, to: INTEGER; font: Font);
…
END;
http://oberon2005.oberoncore.ru/paper/p_ext.pdf
Мне кажется что было бы полезно определять заголовки процедур прямо в описании рекорда(в обязательном порядке), чтобы сразу видеть какие методы объявлены/реализованы в данном модуле.
-
Единственный минус - это ломает обратную совместимость.
-
Насчет интерфейсов советую посмотреть: 2.7 Описания (definitions) (https://docs.google.com/file/d/0B1nt1x4TPu-XeW5Yc1Z0RjZESlU/edit)
-
Мне кажется что было бы полезно определять заголовки процедур прямо в описании рекорда(в обязательном порядке), чтобы сразу видеть какие методы объявлены/реализованы в данном модуле.
Да, мне тоже хочется видеть все в одном месте. И мне кажется никакой внешний инструмент не заменит такого объявления. Поэтому я и предложил интерфейсы в таком виде :) Но сейчас склоняюсь все-таки к проверенному ББ-варианту - для начала.
-
Представить такой внешний инструмент проще, чем его реализовать, и это будет не легче, чем допилить основной компилятор, но во многом похоже по сути
-
vlad, не было мысли добавить адовские комментарии?
-
vlad, не было мысли добавить адовские комментарии?
В смысле однострочные "--" или что?
-
Угу.
-
Угу.
Ну однострочных комментариев не хватает, конечно, но пока можно и без них. И сделать совершенно понятно как.
-
> Да, мне тоже хочется видеть все в одном месте.
Посмотрите на объекты в Active Oberone
test = OBECT
var1: INTEGER;
var2: INTEGER;
PROCEDURE method1(VAR param1: INTEGER);
BEGIN
END method1;
...
END test;
-
Лично я против кода процедур в описании рекорда/объекта. Только заголовки! :)
-
Посмотрите на объекты в Active Oberone
Смотрел. Во-первых (как я уже говорил), хочется только декларации, без потрохов. Во-вторых, оно сразу подразумевает если не многопроходность, то серьезные изменения в реализации компилятора. Оно, конечно, все равно случится в какой-то момент. Но этот момент хочется оттянуть - чтобы к тому времени меньше кода было на JS (который рефакторить без страданий ну никак нельзя).
-
Итак, свершилось. Методы добавлены: https://github.com/vladfolts/oberonjs/wiki/eberon-methods. Код с помощью них написан: https://github.com/vladfolts/oberonjs/blob/master/src/ob/Types.ob.
Замечания и предложения приветствуются.
-
Новые впечатления от переписывания:
- конструкторы полезны в том числе и для инициализации базового класса.
- так уж выпало, что переписывание совпало по времени с очередной дозой восторга по поводу обероновского синтаксиса на сайте Зорко. Ну очень смешно. Исходник после переписывания увеличился в два раза... А ведь это был JS, даже не питон... ;)
-
... так уж выпало, что переписывание совпало по времени с очередной дозой восторга по поводу обероновского синтаксиса на сайте Зорко. Ну очень смешно. Исходник после переписывания увеличился в два раза... А ведь это был JS, даже не питон... ;)
Ну так оберонщегам надо же как-то обосновать свои 500 строк кода в день ))
-
Итак, свершилось. Методы добавлены: https://github.com/vladfolts/oberonjs/wiki/eberon-methods. Код с помощью них написан: https://github.com/vladfolts/oberonjs/blob/master/src/ob/Types.ob.
Замечания и предложения приветствуются.
PROCEDURE Record.initializer(cx: Context.Type): JsString.Type;
RETURN JsString.concat(JsString.concat(JsString.concat(
JsString.make("new "),
cx.qualifyScope(SELF.scope)),
SELF.cons),
JsString.make("()"))
END Record.initializer;
А куда делся BEGIN? о_О
-
А куда делся BEGIN? о_О
BEGIN не нужен, если нет statements. См. грамматику.
-
А куда делся BEGIN? о_О
BEGIN не нужен, если нет statements. См. грамматику.
Нарушается правило наименьшей неожиданности... :P
-
Нарушается правило наименьшей неожиданности... :P
Это все ерунда. Вот невозможность поставить точку с запятой после RETURN и после последного филда в рекорде - да, доставляет ;)
-
Нарушается правило наименьшей неожиданности... :P
Это все ерунда. Вот невозможность поставить точку с запятой после RETURN и после последного филда в рекорде - да, доставляет ;)
Да, это самые частые мои "опечатки" при например портировании компилятора на Oberon rev 2013 (в другое окружение). Ну и при написании биндингов тоже.
-
Нарушается правило наименьшей неожиданности... :P
Это все ерунда. Вот невозможность поставить точку с запятой после RETURN и после последного филда в рекорде - да, доставляет ;)
А вот это надо пофиксить! ))
-
А вот это надо пофиксить! ))
Не, если дело дойдет до синтаксиса, то надо выкидивать всю архаику нафиг. Вместе с капсом. Точки с запятой тут как мертвому припарка...
-
А куда делся BEGIN? о_О
BEGIN не нужен, если нет statements. См. грамматику.
В грамматике неточность. По ней вдобавок получается, что после StatementSequence перед RETURN не должно быть точки с запятой, что опровергают все примеры.
По хорошему должно быть как-то так:
ProcedureBody = DeclarationSequence [ BEGIN ( StatementSequence [";" RETURN expression] | RETURN expression ) ] END
-
А куда делся BEGIN? о_О
BEGIN не нужен, если нет statements. См. грамматику.
В грамматике неточность.
Я бы не стал "исправлять" по своему разумению вполне формальную оригинальную запись. Потому что так можно далеко зайти (особенно если иметь "непаскальный" опыт).
По ней вдобавок получается, что после StatementSequence перед RETURN не должно быть точки с запятой, что опровергают все примеры.
Именно так. Точка с запятой может отсутствовать. Поэтому примеры ничего не опровергают. Видимо Вирт по привычке всегда ее ставит (RETURN как не-statement появился недавно) .
По хорошему должно быть как-то так:
По-хорошему - идиотизм запрещать точку с запятой после последнего филда и после RETURN. Здравый смысл принесенный в жертву формалистике.
-
А куда делся BEGIN? о_О
BEGIN не нужен, если нет statements. См. грамматику.
В грамматике неточность. По ней вдобавок получается, что после StatementSequence перед RETURN не должно быть точки с запятой, что опровергают все примеры.
По хорошему должно быть как-то так:
ProcedureBody = DeclarationSequence [ BEGIN ( StatementSequence [";" RETURN expression] | RETURN expression ) ] END
Кстати, пока я на это дело при компиляции Виртовский сорцов не напарывался.
-
А куда делся BEGIN? о_О
BEGIN не нужен, если нет statements. См. грамматику.
В грамматике неточность.
Я бы не стал "исправлять" по своему разумению вполне формальную оригинальную запись. Потому что так можно далеко зайти (особенно если иметь "непаскальный" опыт).
По ней вдобавок получается, что после StatementSequence перед RETURN не должно быть точки с запятой, что опровергают все примеры.
Именно так. Точка с запятой может отсутствовать. Поэтому примеры ничего не опровергают. Видимо Вирт по привычке всегда ее ставит (RETURN как не-statement появился недавно) .
По хорошему должно быть как-то так:
По-хорошему - идиотизм запрещать точку с запятой после последнего филда и после RETURN. Здравый смысл принесенный в жертву формалистике.
Необязательная точка с запятой легко оформляется в формализме, ничего приносить в жертву не нужно.
То, что я мел ввиду "по-хорошему" - это корректное отображение в формальной записи авторской мысли, разумеется, как понял её я. Точка с запятой согласно той грамматике именно обязанна отсутствовать, а ошибиться сразу во всех примерах было бы сложно кому угодно, тут элементарная логика. Я уже обращал внимание http://oberspace.dyndns.org/index.php/topic,571.msg19406.html#msg19406 (http://oberspace.dyndns.org/index.php/topic,571.msg19406.html#msg19406), что в отредактированных материалах Никлауса Вирта присутствует небрежность, стоит ли к этому относиться с непонятным злорадством? Никто Вирта ни "божеством", ни "мэтром", ни чем либо ещё из припасённых за пазухой саркастичных эпитетов не считает.