Конференция "KOL" » SOL (Соль) = Simple Objects Language
 
  • misha_shar © (22.12.07 14:14) [60]
    Хорошо я так понял идет обсуждение спецификаций языка.
    Предлагаю сделать набор операций расширяемым. Где нибудь в
    начале программы дополнительным операциям сопоставить программы
    выполнения этих операций определенного типа. А затем компилятор
    заменяет операцию на обращение к функции.
  • Vladimir Kladov © (22.12.07 19:03) [61]
    Я бы рад принять предложение, но там это уже как бы есть. Я все-таки предлагаю ознакомиться. Хотя для начала в том варианте который краткий, но не самый краткий.
  • Vladimir Kladov © (22.12.07 19:06) [62]
    Сегодня я выложил версию 14. Добавил про сравнение типов и чуть более подобно описал, что такое шаблоны (абстрактная запись алгоритмов без привязки к конкретным типам данных) и как с ними бороться. Английский уже 2/3 готовы. Надеюсь успеть за выходной.
  • vpbar © (23.12.07 00:31) [63]
    Приветствую, тезка.
    Эээ.
    Как я понял от GOODWELL почти ничего не осталось. Жаль . Он мне больше нравился. Я даже некоторые идеи подогнал для своего мега языка :)

    Теперь по теме

    > В языке Паскаль существует правило, запрещающее изменять
    > значение счётчика цикла в теле цикла. Возможно, это сделано
    > для того, чтобы не мешать компилятору оптимизировать выполнение
    > цикла. В Си такого ограничения не существует. Думается,
    > что в простом языке такого ограничения так же не должно
    > быть. Не изменяется - хорошо, изменилось - в чём проблема?
    >  Ведь всё равно данное ограничение обходится в том же Паскале
    > переписыванием цикла типа for на цикл типа while.

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


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

    Сильный аргумент :). Но непоследовательно в одном случае усложнять компилятор (определение типов) а в другом упрощать. Вложеные функции иногда удобны хотя бы для ограничения видимости.


    > Посмотрите, что произойдёт в случае, если мы так же введём
    > в язык ограничение на число передаваемых функции параметров.
    >  Например, одним-единственным возможным параметром (упрощать,
    >  так упрощать!).

    Я предлагаю еще упростить. Сделать функцию объектом с методом напимер execute и полями, которые по сути параметрами будут. (В данном случае это не стеб). Идею дарю. Конечно компилятор немного усложнится (ничего в другом можно упростить :) ) - ему придется проверять инициализированы ли все поля до вызова (хотя тут и значения по умолчанию могут быть). Зато есть плюсы. Например повторный вызов если изменен только один параметр короче будет.
    А параметры константы, in out ref и все прочие декларации как? Как описание полей структуры? Или функция возвращает тоже структуру?

    Все. жена пришла из душа :)
    Удачи в новом начинании.
  • Vladimir Kladov © (23.12.07 02:10) [64]

    Вложеные функции иногда удобны хотя бы для ограничения видимости.

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


    данном случае это не стеб). Идею дарю
    А в остальных случаях - стёб? Спасибо но мне не надо такой идеи. Дочитаете до моих методов, поймёте что в этом смысле они у меня как в Обероне - просто функции.

    А параметры константы, in out ref и все прочие декларации как? Не неужны. Читайте внимательно. Надо, чтобы изменялось внутри функции - передаём указатель. Если передаём массив по значению, и внутри он изменяется (а раз по значению, то не изменяется снаружи), компилятор ругается, но делает как приказано. Передаётся по значению, но не изменяется, компилятор передаёт указатель неявно. Хотим, чтобы массив изменился на самом деле - передаём указатель на весь массив. В 2х последних случаях компилятор молчит, и передаётся указатель.

    Или функция возвращает тоже
    структуру?
    Не вижу никаких проблем с возвращением структуры или массива.
  • Vladimir Kladov © (23.12.07 10:21) [65]
    English verion of short (but not very short) specification is ready:
    http://kolmck.net/sf/SOL%20-%20short%20description.English.htm

    Sorry for my English but you know that it is not my native language.

    Заодно русский вариант поправил. Кстати, если кто-то (вдруг) осилил полное описание и обнаружил, что в коротком чего-то важное забыто или не совпадает, пожалуйста, подскажите на недостатки.
  • vpbar © (23.12.07 15:33) [66]
    Стеб только тут "Сильный аргумент :)." В остальном просто попытка понять идею путем уточнения непонятных моментов.  

    > Кроме того, я советую задуматься, как (насколько эффективно)
    > вложенные функции реализуются в машинном коде.

    Насколько я понял главной целью языка не является упрощение и оптимизация копмилятора. К томуже вложеность тут не играет роли. Разве в Соль нельзя вызвать функции рекурсивно? При том что "Область определения функции - весь модуль" они могут тоже довольно запутано вызываться. Так что, имхо, это не агрумент.


    > Читайте внимательно. Надо, чтобы изменялось внутри функции
    > - передаём указатель.
    Читаю. Не все еще прочитал, но вряд ли дальше это описывается.
    Допустим есть такое определение  на неком гипотетическом языке. Но суть думаю понятна будет.
    function name(s:string; in x:Type; inout io:Type  ;out e:TError):Type;
    Параметр x должен быть инициализирован до вызова, не может менятся внутри функции. e - должен быть инициализирован внутри функции.  io - передается по ссылке. Т.е. это дает определенный контроль за вызовом функции.
    Как это описать в Соль? Я пока не нашел. В принципе в некоторых случаях можно все "выходы" функции возвращать в результате-кортеже. А то, что какойто входной параметр может менятся у вас указывается ссылкой на него. Но вообщем я не пришел к заключению как лучше (У меня тоже возникла похожая идея. Обобщить функцию до отношения между кортежами).
  • homm © (23.12.07 15:46) [67]
    > [66] vpbar ©   (23.12.07 15:33)
    > К томуже вложеность тут не играет роли.

    А теперь представь, что вложенная функия орбащается к переменным (стеку) родительской функции. Притом сама выделяет некое количество переменных на стеке. Вопрос: как еще, кроме как трассировкой стека функция может получить доступ переменным родительской, если она вызывается рекурсивно? (может я ошибась? что-то правда все грустно получается…
  • vpbar © (23.12.07 16:08) [68]
    >>homm ©   (23.12.07 15:46) [67]
    Ээ. Дык в делфи например это работает. И без всякой трасировки.
  • homm © (23.12.07 16:13) [69]
    > [68] vpbar ©   (23.12.07 16:08)
    > И без всякой трасировки.

    Ну а как, тогда? Может указатель на начало стека родительской процедуры всегда сохраняется?
  • vpbar © (23.12.07 16:14) [70]

    >  Как только последняя такая ссылка исчезает...., объект разрушается.
    >  При этом все "слабые" указатели, указывающие на этот объект,
    >  автоматически получают значение NIL.

    А вот это мне нравится. Хотя тут было подобное обсуждение, и со мной стали спорить, что такое нельзя сделать.
  • Vladimir Kladov © (23.12.07 16:44) [71]
    Не совсем трассировкой, но суть в том, что на каждой вложенной процедуре в стек подаётся ещё один скрытый парамтр - вектор из базовых адресов локалов каждой из процедур верхнего уровне, к переменным которой ей может понадобиться обратиться. К тому же обращения получаются всегда косвенные через базы, которая лежит на стеке, что весьма накладно по сравнению с прямым доступом к локальным переменным.

    function name(s:string; in x:Type; inout io:Type  ;out e:TError):Type;
    Тут штука в том, что по неизвестной мне причине разработчики компиляторов избегают анализа между процедурами, т.е. стараются сделать так, чтобы компилятор рассматривал каждую процедуру/функцию как "чёрный" ящик. Это наверняка упрощает дело, но я собираюсь выполнять такой анализ. Т.е. когда компилируется A, вызывающая B, то даже если ещё не готов результат компиляции B полностью, для неё уже точно будет известно о каждом параметре, есть ли (возможны ли) попытки изменить параметр внутри функции B, или если не в ней самой, то в тех функциях, которые она вызывает, передавая адрес своего параметра вместо самого параметра в качестве параметра тех функций.

    Соответственно, компилятору это и так известно. Человеку это и не интересно, для него известно точно, что если передаётся адрес явно, то параметр может измениться (он хочет чтобы он изменился), а если передана просто переменная, то измениться она не может. Это упрощает, хотя может быть и не очень (и совсем не) эффективно, если передаётся большая структура или массив по значению, и модифицируется внутри (но я думаю и с этим случаем маленько побороться - техникой copy-on-write, например).

    Так, я кажется забыл написать, что я собираюсь инициализировать все переменные значением по умолчанию. В голове-то оно у меня есть, потому что уже отвечал в письме товарищу одному, да. Но, видимо, я забыл подыскать место в текстах, куда это впихнуть. В общем, это обязательный момент, и касается всех переменных, локальных и глобальных, и не только указателей. Оставался ещё открытым вопрос насчёт динамических массивов, будут ли они неявно инициализироваться по Allocate (наверное, всё-таки нет, кроме случая массива указателей, указатели всегда должны быть равны NIL изначально). Если инициализатора у переменной нет, то нулями (но тогда 0 должен быть в диапазоне, а если нет нуля в диапазоне, и нет инициализатора, то ругаться будет и откажется дальше компилить).

    Идея не в том, чтобы проинициализировать всё, даже то, что инициализацировать не требуется, а в том, чтобы задать формально некоторые начальные условия, от которых пляшет комилятор. Т. е. если он видит что переменная передана из A в B неинициализированной, а она в B требуется уже в нормальном состоянии, то он её проинициализирует до передачи значением по умолчанию. Само собой, нет смысла инициализировать переменную, начальное значение которой не нужно, и ей будет присвоено значение (в B).

    При этих условиях const, in, var и out - совершенно ненужный семантический мусор для параметров. Немножко междупроцедурного анализа, и всё само собой устаканивается (с одним различием - указатель или значение). Разумеется, надо контролировать весь граф вычислений, что кому и когда присваивается, но на это компилятор и нужен, по-моему.

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

    Вот интересно: никто даже не трогает проблему сборщика мусора. Наверное, программеры Delphi просто мало знают, о чём идёт речь, и почему моё решение проблемы GC - это кровавая революция с отрубанием голов всех ныне сущих языков с GC, включая Java, Oberon и всего .NET.

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

    Я даже понимаю, что проблема шаблонного программирования не понятна программерам на Delphi. Ну нету в Delphi шаблонов, и не надо: обходимся же динамическим полиморфизмом (указатель функции как параметр алгоритма).

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

    В общем, пойду-ка я и правда попробую составить что-то типа сравнительных таблиц. Я не уверен, что хорошо знаю некоторые малораспространённые языки, вроде Ады или Оберона, но если что, меня поправят.
  • vpbar © (23.12.07 17:05) [72]

    > Так, я кажется забыл написать

    Кажеться, хотя я почти догадался что это так. Про указатели есть то что они инициализируются нилом, а про остальное нет.

    > При этих условиях const, in, var и out - совершенно ненужный
    > семантический мусор для параметров.
    Может быть, может быть. Просто мне больше нравится сказать компилятору что данный out парамеот должен обязятельно быть изменен в функции.


    > Функциональными языками попахивает

    Еще как :).

    >  никто даже не трогает проблему сборщика мусора

    Вы выложите тему в прочее, там Зотов (если не ошибаюсь может и Шевченко, давно было это) с вами поспорит. По-поводу неопределенности вызова деструктора, финализаторов и прочего. По мне у вас интересное решение. Но пока я не представляю как будут организованы сложные структуры данных с вашими разношерстными указателями. У сборщик мусора есть - и хорошо. Нет - и тоже не проблема, имхо.

    Ну нету в Delphi шаблонов, и не надо
  • vpbar © (23.12.07 17:27) [73]
    Прошу прощения. Тыкнул не туда. Продолжаем.

    > Ну нету в Delphi шаблонов, и не надо

    Надо. Мне, по крайней мере. Но тут у вас, вроде ничего революционного. В том же nemerle и haskel-е похожий подход.
  • Vladimir Kladov © (23.12.07 17:27) [74]
    vpbar>  При этом все "слабые" указатели, указывающие на этот объект,
    >  автоматически получают значение NIL.

    А вот это мне нравится. Хотя тут было подобное обсуждение, и со мной стали спорить, что такое нельзя сделать.

    Где было обсуждение?

    Сделать можно, 2мя способами. 1) "умные указатели". Каждый блок в памяти (который в SOL всегда является динамическим объектом с VMT, будь он структура, или динамический массив) обратно ссылается на smart-pointer (это термин С++, как они там с этим борются), по памяти никак не перемещаемый, а все указатели на объект указывают не на объект, а не смарт-поинтер. Надо присвоить NIL - это значение записывается в smart-указатель, и вуаля.

    Мне это решение не очень нравится, т.к. требуется двойная косвенность.

    2) Теперь простой вариант, хотя и кажется на первый взгляд неэффективным. Каждый указатель - это 2 адреса: один на сам объект второй - на следующий в циклической цепочку указателей на тот же самый объект. И опять, динамический объект обратой ссылкой смотрит на один из указателей, которые смотрят на него. А т.к. все такие указатели образуют кольцо, то они все перечислимы, сколько бы их ни было. И всегда можно пройтись по кольцу, и присвоить всем указателям NIL (причём, обоим адресам в этих указателях, разорвав кольцо).

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

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

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

    (Чего я это пишу по новой? Это уже всё написано в большом тексте).
  • homm © (23.12.07 17:35) [75]
    > [74] Vladimir Kladov ©   (23.12.07 17:27)
    > Впрочем, можно и оба варианта реализовать, и просто переключаться
    > в опциях.

    Вот поменьше бы опций :)
    А что вернет sizeof(p) во втором варианте?
  • Vladimir Kladov © (23.12.07 17:41) [76]
    > При этих условиях const, in, var и out - совершенно ненужный
    > семантический мусор для параметров.
    Может быть, может быть. Просто мне больше нравится сказать компилятору что данный out парамеот должен обязятельно быть
    изменен в функции.

    Ну и скажите, описывая параметр как указатель. Если не изменяется, всегда передавайте по значению. Результат будет тот же: компилятор использует указатель для агрегатов. Проблемы?

    При сборщике мусора пользовательский деструктор не нужен для освобождения памяти. Свой деструктор может понадобиться только для освобождения инкапсулированных ресурсов Всё у меня написано. Я разве собираюсь с кем-либо спорить? Я собираюсь это реализовывать. Мое мнение простое: системы с обобщённой сборкой мусора не годятся для рил-тайм систем. И чем больше данных может лежать в куче, и чем более интенсивно она используется, тем меньше годятся. Поэтому и искал способ, как быть если нужна сборка мусора, но без сборщика.

    Не надо шаблонов? Может и не надо. Copy-paste тоже метод, хотя вместе с умножением кода умножаются и ошибки. Причём не линейно, а гораздо быстрее.

    Функциональные языки меня не интересуют. Привычка. А может, склад ума такой. И неэффективные они, кроме некоторых очень узких классов задач вроде моделирования ИИ. Я их не собираюсь отменять, пусть живут. Мне императив нужен, прямой как палка.
  • Vladimir Kladov © (23.12.07 17:46) [77]
    Вот поменьше бы опций :)

    Опции всё-таки бывают нужны. Как-то же надо задавать компилятору, что мы от него хотим: быстроту или компактность, дефрагментацию или нет, и вообще для какой платформы и в каком виде результат выдавать?


    А что вернет sizeof(p) во втором варианте?

    Что такое sizeof, зачем он вообще нужен и с чем его едят в языке, в котором размером данных занимается компилятор и самый нижний уровень программирования?
  • vpbar © (23.12.07 17:48) [78]
    >>Vladimir Kladov ©   (23.12.07 17:27) [74]

    > Где было обсуждение?

    Гдето в "прочее", но давно (месяца два назад). И щас не могу найти. Видно в архив ушло.

    > Сделать можно
    Дык я знаю.
    homm ©   (23.12.07 17:35) [75]
    А ничего. Ибо sizeof нет в Соль
  • homm © (23.12.07 17:48) [79]
    > [77] Vladimir Kladov ©   (23.12.07 17:46)
    > Что такое sizeof, зачем он вообще нужен и с чем его едят
    > в языке, в котором размером данных занимается компилятор
    > и самый нижний уровень программирования?

    Хороший ответ :) Наверное все же застявлю себя седня прочесть описание :)
 
Конференция "KOL" » SOL (Соль) = Simple Objects Language
Есть новые Нет новых   [134431   +15][b:0.001][p:0.001]