LINUX.ORG.RU

язык мечты


0

1

Добрый вечер!

Теперь всё ясно: Common Lisp - это кал. О чём же теперь мечтать перед сном? Об обратимом препроцессоре? Это можно, но как-то мало. Хочется мечтать о большем. Например, о таком:

1. Одно понятие пространства имён. В "обычных" языках есть пространство имён как namespace, а есть виртуальное пространство имён, которое возникает внутри данного scope. Или пр-во тех имён, которые можно писать после точки, если переменная имеет тип. Обобщить до одного понятия, придать средства управления.

2. Паскаль - порядка 10000 строк в секунду. Хороший мальчик. Синтаксис должен быть простым для компьютера и читаемым для человека. При этом должна быть возможность делать быстрый и компактный код. Поэтому - строгая типизация. Есть тип variant (можно назвать его t для краткости) - он делает Паскаль вполне похожим на лисп по гибкости работы с типами, при условии, что в таком стиле написаны библиотеки.

3. Сборку мусора - в мусорное ведро вместе с JIT. Всё это - разводка на бабло, чтобы покупали новое железо. Вместо этого безопасность дадут декларации работы с памятью: function foo(caption:string):fresh(widgets.form); Что-то мне подсказывает, что весьма простым способом можно с помощью таких деклараций и выводов из них построить работу с памятью без сборщика мусора и с высокой степенью автоматизации (хотя могу ошибаться).

4. Язык должен быть сначала императивным, а потом уже можно строить всё остальное. Для функциональщиков покатит декларация pure. Если в функции f вызываются только чистые функции, то и сама эта функция чиста. Значит, декларацию pure легко проверить на этапе сборки. Соответственно же, декларации const тоже можно проверить (это ещё С умеет делать).

5. Встроенный тип variant. Один, а не 10, как в "некоторых других языках".

6. Макросы не хуже лиспа. А иначе - какой смысл.

7. Конечно же, генерация исполняемого кода в рантайме. Можно через .so, как GCL.

8. Встроенный FFI типа CFFI-GROVEL. Конечно, только С.

9. read-print как в лиспе. Ну и eval к ним (т.е., code=data)

10. Вменяемый синтаксис, но простой. Представление code=data - это промежуточное представление. Для человека оно преобразуется по простым, но гибким правилам. Ну и что-то вроде readtable, как в лиспе. А может,что-то более стройное и менее хакерское.

11. Не ОО. функции, примитивные типы и структуры. И хватит. Вместо ОО - функции как объекты первого класса. ОО должно быть маленькой и лёгкой библиоткой (стиля JavaScript, наверное). CLOS - это пример как делать не надо. Про С++ лучше вообще не говорите.

12. Замыкания... Наверное, состояние в них должно быть явным.

13. Встроенный codewalker.

14. Наверное,кроссплатформенность. Причём, в определении платформы должны содержаться и базовые элементы работы с GUI. Платформа может быть в чём-то неполной (например, не иметь примитивов для работы с файловой системой, eval или compile) и это не должно влиять на общую работоспособность - пусть работает то, что может работать.

Эх, мечты-мечты... Ладно, спокойной ночи.

anonymous
Ответ на: комментарий от anonymous

>> Я вроде и не говорил о ядрах на Cyclone (он умер, вообще-то). А вот то, что на нем можно писать драйверы в _существующие_ ОС - это доказано практикой. Проделаешь такой трюк с Хаскелем?

> загружать через FFI, не?

Ммм... загружать ядро в Хаскел-программу, и использовать его через FFI?

/me медленно выпадает в осадок крупными хлопьями

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Ммм... загружать ядро в Хаскел-программу, и использовать его через FFI?

хотя наверно и так тоже пойдёт. Хаскель-программа -- это ядро по спеке L4, "загружаемое ядро" -- это L4:Linux

anonymous
()
Ответ на: комментарий от anonymous

> не, ядро загружает хаскель-программу

Ага, а еще ядро выделяет хаскель-модулю 4метра стека, и позволяет запускать GC.

> которая через FFI дёргает драйвера ядра

А что сама Хаскель-прога делает?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> А что сама Хаскель-прога делает?

символизирует. Исполняемую спецификацию драйвера, например.

И да, возможно не нужен FFI, хватит нативного L4 API (реализованного на хаскелле).

> Ага, а еще ядро выделяет хаскель-модулю 4метра стека, и позволяет запускать GC.

да, вот с контролированием хаскель-рантайма проблема. Но наверное, решаема примерно как realtime GC :))

Гибрид какой-то получается, вроде MacOSX Mach+BSD, Glendix Plan9+Linux...

anonymous
()
Ответ на: комментарий от anonymous

>> А что сама Хаскель-прога делает?

> символизирует.

ыыыыыыыыыыыыыыыыыыы

> Исполняемую спецификацию драйвера, например.

Мне в ядре драйвер нужен, а не спецификация.

>> Ага, а еще ядро выделяет хаскель-модулю 4метра стека, и позволяет запускать GC.

> да, вот с контролированием хаскель-рантайма проблема. Но наверное, решаема примерно как realtime GC :))

Вот решишь ее, тогда заходи.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Мне в ядре драйвер нужен, а не спецификация.

дык исполняемая.

> Вот решишь ее, тогда заходи.

надо долго курить region based allocator из Cyclone...

anonymous
()
Ответ на: комментарий от anonymous

>> Мне в ядре драйвер нужен, а не спецификация.

> дык исполняемая.

дык спецификация

> надо долго курить region based allocator из Cyclone...

...и наконец понять, что он не заменит сборку мусора в Хаскеле

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> дык спецификация

ещё раз дык

> ...и наконец понять, что он не заменит сборку мусора в Хаскеле

дык нам и не нужен полноценный хаскель с полноценным GC. Нам нужно то статическое управление памятью, на которое отображается DSL драйвера. Драйверу хватит и "статического CG". Скажем, "исполнятор" исполняемой спецификации может запустить её под хаскеллем или оттранслировать в что-то более детерминированное (в первом приближении, ghc => gcc). А там другая, чем в модельном GHCi GC, другой "JIT", и т.п. То есть, полезен был бы не ghc => gcc, а скажем ghc => cyclone => gcc.

anonymous
()
Ответ на: комментарий от anonymous

> Нам нужно то статическое управление памятью, на которое отображается DSL драйвера. Драйверу хватит и "статического CG". Скажем, "исполнятор" исполняемой спецификации может запустить её под хаскеллем или оттранслировать в что-то более детерминированное (в первом приближении, ghc => gcc). А там другая, чем в модельном GHCi GC, другой "JIT", и т.п.

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

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Сначала надо будет выучить хаскелль :)) Потом Cyclone. Потом написать DSL. ^U

Страна советов закончилась, придётся самому делать?
Скажем, взять какой-либо драйвер с открытыми спеками под линукс, реализованными процентов на 30%, составить по спекам DSL, дальше делаем roundtrip разработку в духе: берём L4:Linux, и полудоделанный работающий старый драйвер. Берём модельную L4 на хаскеле, которая умеет запускать L4:Linux, в которой драйвер, который построил Джек. Пишем DSL, получаем спецификацию на API драйвера. Реализуем первые 30% спеки, тестируем под L4 на хаскелле, сравниваем этот прототип со старым + L4:Linux. Реализуем остальные 70% протипа. Нажимаем большую красную кнопку, и опа -- кодогенератор наваял драйвер для L4:Linux, другую синюю кнопку -- и для Linux просто. вот как-то в таком акцепте.

anonymous
()
Ответ на: комментарий от anonymous

> В sbcl я уже нашёл пару багов и просто корявостей, а в ccl эти места - чистые.

Зато в SBCL нормальный компилятор, в отличие от CCL :)

mv ★★★★★
()
Ответ на: комментарий от ero-sennin

> из полноценных лиспов сейчас есть разве что Clojure

Ну-ка, ну-ка... Что не так в SBCL и CLISP?

mv ★★★★★
()

> Теперь всё ясно: Common Lisp - это кал.

Поздравляю, тест на дебилизм ты прошёл.

> Паскаль - порядка 10000 строк в секунду.

Не понимай... Русский говорить...

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

У тебя каша в голове. Типизация и синтаксис оччень слабо связаны.

> Сборку мусора - в мусорное ведро вместе с JIT.

Ню-ню.

> Вместо этого безопасность дадут декларации работы с памятью

Агащазблин.

Сборка мусора пока что - один из немногих механизмов, РЕАЛЬНО облегчающих работу простого программиста.

> Если в функции f вызываются только чистые функции, то и сама эта функция чиста. Значит, декларацию pure легко проверить на этапе сборки.

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

> Встроенный тип variant.

Душить.

> Макросы не хуже лиспа. А иначе - какой смысл.

Да и так не особенный. Макросы - отличная штука, но если сам язык достаточно мощный, они становятся не нужны в 95% случаев.

> Конечно же, генерация исполняемого кода в рантайме.

Тормоза...

> Встроенный FFI типа CFFI-GROVEL. Конечно, только С.

Э-э-э... Почему только C? Откуда такое странное ограничение?

> read-print как в лиспе.

REPL. Есть в каждом уважающем себя языке.

> Ну и eval к ним (т.е., code=data)

Если eval в REPL-е, то к code=data отношения не имеет. Если в готовой программе - опять получаем неслабые тормоза.

> Вменяемый синтаксис, но простой.

а) Повторяешься.

б) Синтаксис идёт лесом.

> Не ОО.

Угу.

> Замыкания... Наверное, состояние в них должно быть явным.

Ты офонарел? Это ж сколько писанины будет.

> Встроенный codewalker.

Не понял.

> Наверное,кроссплатформенность.

Почему "наверное"?

Miguel ★★★★★
()
Ответ на: комментарий от Valeriy_Onuchin

Текс. Море ссылок... Хватит на оба выходных, но личная жизнь, вроде бы, складывается.

На Хаскель я забил изначально, всё пропускаю. Лицемерный язык...

D выглядит получше. Много лишнего, но есть главное, что нужно: модульная система. Правда, я не очень понимаю, как тогда линковаться к ядру - оно ведь во многом заложено на сишный препроцессор (как я понял, там его нет). В лиспе я столкнулся с этой проблемой (при разработке примера на fuse). Я применил аж два средства для её решения: SWIG и CFFI-GROVEL. Первое средство вроде крутое, но всё, что оно не понимает, оно считает указателем. Т.е., похоже, что я должен найти все определения типов и перенести их в .i файл (SWIG не особо понимает, что два типа с разным названием как-то выведены друг из друга). А эта задача какая-то не совсем трививальная - похоже, что она тянет за собой проход по всему пути определений через #define разных типов в ядре (или я что-то недопонял). Впрочем, в этом вопросе я почти что полный чайник.

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

Кроме того, если оба средства применять вместе, то, в каком порядке их не запускай, всё равно будет плохо.

Теперь я думаю доделать CFFI-GROVEL, чтобы он определял свойства полей структуры. И тогда будет уже нормально.

С этой же проблемой придётся столкнуться любому, кто будет что-то делать не на С. Мне вообще-то интересно, как её решают в Питоне или в других FFI. Потому что проблема - не совсем простая, по-моему. Хотя это вроде бы не про то (или почти не про то).

Если эта проблема в D не решена, то он имхо ничем не лучше объектного Паскаля (учитывая, что можно прикрутить препроцессор).

> зачем нужен variant, когда есть auto

Кроме локальных переменных, есть ещё и параметры функций. В лиспе любые функции изначально являются полиморфными. Правда, при неправильном типе они породят исключение, а при (declaim (optimize (speed 3))) могут порушить программу. Так что всё довольно честно. Хочешь гибкости - поставь внутри typecase. Хочешь скорости - одень каску. Auto так не может, а variant - может (или я не догнал). Или если мы циклом идём по полиморфному массиву и одна и та же переменная может иметь разные типы.

Что такое code walker? Вот есть кусок кода:
{ int i=1; { int i=4; }}
code walker должен при проходе внутреннего блока быть способен поучаствовать в таком спектакле:
Я: какие у нас есть переменные?
СW: i типа int, инициализирована четвёркой
Я: это то же i, которое вот в этом блоке (даю ссылку на внешний блок)?
CW: нет, это другая.
Для способности это делать, нужно частично разобрать семантику кода. И это _далеко_ не то же самое, что просто пройти по дереву.

> а если "замена readtable" будет двухсторонним, вроде "линзы" в Harmony/.

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

Так. Что делаем с памятью?

den73 ★★★★★
()
Ответ на: комментарий от den73

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

Итак, у нас есть 4 области для храненияобъектов:
- недвижимая память (она же статическая, но надо, чтобы начиналась не на букву с)
- стек
- куча
- плошадка, по которой ходит дворник и собирает мусор

Про каждую _переменную_ (локальную, глобальную или параметр) нам нужно знать, из
какой области (областей) может быть объект, который живёт в этой переменной.

В худшем случае, мы не можем знать. Тогда всё решается в рантайме. Лузерский объект должен знать свою область во время рантайма. Это можно либо вычислить простым алгоритмом по адресу (если повезёт), либо пометить объект (поместить его в коробку или оставить в нём два бита под это дело). При создании ссылки на такой объект, нужно проявить много осторожности.
н->н - всё хорошо
с->н - хорошо
к->н - всё хорошо
п->н - нужно, чтобы сборщик мусора не переложил нас куда-нибудь в другое место
н->c - извещаем о себе деструктор кадра стека. О своей погибели - тоже. Если мы пережили кадр стека, он перед смертью порождает ошибку времени выполнения. "Не доставайся же ты никому" (может быть, можно сделать подсчётом ссылок на кадр стека)
н->к - извещаем о себе менеджер кучи. О своей погибели - тоже. Если мы живы в момент вызова деструктора, то ошибка времени выполнения (может быть, это можно сделать подсчётом ссылок).
н->п - addRange в терминах D (http://digitalmars.com/d/2.0/memory.html#newdelete)
с1->с2 - если с2 - глубже по стеку, чем с1 - то пофигу. Если наоборот - известим кадр стека.
с->к - подсчёт ссылок на к.
с->п - addRange
к->* - аналогично с->*
п->* - аналогично с->*

Т.е., путём некоторого (на самом деле, значительного) утяжеления программы, можно гарантировать (если я ничего не путаю), что мы не запишем в туман. Это уже хорошо, хотя нельзя гарантировать отсутствие утечек.

Теперь пытаемся улучшить.

Заведём для переменных, параметров и возвр. значений атрибуты, ограничивающие области памяти, на которые эти переменные, параметры, возвраты могут ссылаться.
Теперь в переменную можно записать не всё, что угодно. Атрибуты могут иметь вид:

"я (переменная) могу хранить только объекты из области к или с"
"я (функция) не создаю ссылок на этот аргумент, которые существуют после моего возврата"
"я (функция) обязуюсь удалить этот аргумент с помощью free"
"я (функция) возвращаю значение, выделенное на куче"
"я (функция) могу создать ссылку на этот аргумент, но только из области п"
"я (функция) не меняю этот аргумент (const в смысле С)"

Получается некоторая группа (или другая алгебраическая структура) допустимых действий.

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

int foo(garbage const char* c) {
return atoi(c);
}

В этом случае нужно сделать код:
int ___foo___(const char *c) {
addRange(c); // дабы не собралось в мусор
end_range_initialization: // до этой метки сборка мусора запрещена
int result=atoi(c);
removeRange(c); // пусть становится мусором
return result;
}

если же мы определим
int foo(statically_allocated const char* c) {
return atoi(c);
}

то ничего делать не нужно. Соответственно, если foo у нас принимает даже просто const char * и объявлена при этом inline, то
в коде
int func() {
auto s = new_garbage("asdf");
return foo(s);
}
мы должны подставить первый вариант, т.к. выведение типов говорит, что s находится на площадке мусорщика, а foo ничего плохого с ней не делает. Кроме того, можно сократить два вызова addRange (один из func, другой из foo) в один. В итоге, код станет таким
int ___func___() {
range_handle r;
char *s = new_garbage_internal("asdf",&r); // возвращает объект и range
int result = atoi(c);
remove_range_internal(r);
return result;
}

statically_allocated char *global_s = "asdf";

Заметьте, что вместо new_garbage может стоять любая функция с возвращаемым типом garbage char * - генерируемый код будет аналогичным, только может не получиться избавиться от двойного addRange/removeRange.

Если же мы пишем
void func2() {
auto s = global_s;
return foo(s);
}

то мы знаем, что объект находится в области н и можем сгенерить такой код:
int func2() {
return atoi(global_s);
}

зато вот такой код
void bar() {
global_s = new_garbage("asdf");
}

вызовет ошибку компиляции. Также, как и такой:
void bar() {
global_s = some_grey_function();
}

если some_grey_function() возвращает просто char *, а не statically_allocated char *

Вот как-то так. Что характерно, у нас объекты с известной в статике областью могут быть не лузерскими и существовать не в коробке, а сами по себе и не заботиться о своей жизни. В статике будет доказано, что ничего страшного не случится. А и требуется для того, чтобы наш код был быстрым, безопасным и не жрал много памяти. Хотя как можно иметь одновременно объекты в коробке и без коробки - я ещё до конца не додумал.

Это велосипед?

Спокойной ночи...

den73 ★★★★★
()
Ответ на: комментарий от den73

> Это велосипед?

Это разумно.

Только оно не должно быть захардкожено в язык, а писаться в виде либы, типа boost::shared_ptr. К сожалению, сейчас в С++ все это сделать нельзя, хотя бы потому, что указатель на локальные переменные можно присвоить обычному указателю.

Но других препятствий я не вижу.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

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

www_linux_org_ru ★★★★★
()

Эхъ
мутнорылые хаскелеводы и какой-то наркоман.
во что вы превратили мой лор?

anonymous
()

ИМХО редкий случай здравомыслящих рассуждений... Я б совершенно не так на дело взглянул, тем не менее...

Касательно "языка мечты":

1. За основу взял бы Це. Убавлять ничего не стал бы. Пространства имён итд -- от лукавого, модульности хватит, но см. 3 б).

2. Знаменитые Дийкстовские кострукции добавил бы.

3. Чего там (в Це) явно не хватает:

а) замыканий.

б) квалификации структур, типа директивы using (или как там) в Паскале.

с) атомарных арифметических операций -- типа, инкремента и test-and-set. Больше не надо.

Все остальное (в том числе любимые функциональщиками коллективные операции типа map) сполпинка пишется.

Все это застандартить бы -- всем бы проще было, кроме всяких писателей типа Грэма и деньгоклепателей типа Гейтса...

:-)

Die-Hard ★★★★★
()
Ответ на: комментарий от Die-Hard

есть очень прагматический подход к "языкам"
продемонстрировал "лукавый Билли"  под названием Васик - энто
язык для дебилов,  удивительно простой (гораздо проще русского мата),
 но  который понимает даже младенец - это
эсперанто, английский, на  котором говорят 10ки национальностей
в ЦЕРНе, и который понятен всем( я его когда-то назвал - CERNglish)

они же оТчертили путь, куда должны двигаться все "остальные" языки:
COM/OLE-technology, скриптовость, automation  ...
 и вся планета медленно,  но уверенно движется в этом направлении ...

Всё остальное - "от лукавого", какой бы такой "кашерный" шифр
нам придумать?, чтобы все эти быдлочеловечки недопонимали
всего нашего "величия, превосхдства"

Valeriy_Onuchin ★★
()
Ответ на: комментарий от Die-Hard

> 2. Знаменитые Дийкстовские кострукции добавил бы.

Что за они?

> Все остальное (в том числе любимые функциональщиками коллективные операции типа map) сполпинка пишется.

Без параметризуемых типов это всё будет в лучшем случае убого.

tailgunner ★★★★★
()
Ответ на: комментарий от Die-Hard

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

http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/3449948ab098
3183/5002cd0219e09235?lnk=gst&q=sbcl+hashtable+budden#5002cd0219e09235

Linux_org_ru, синтаксис С я взял только для ясности примера. Он плох тем, что его почти некуда расширять. И, по-моему, всё же его трудно разбирать и скорость компиляции там мала не только из-за #include (кстати, есть же прекомпилированные заголовочники против #include). Синтаксис должен быть проще. Лисп показывает пример синтаксиса, упрощённого до абсурда, но нам туда не надо. 

Я не могу сейчас, наверное, привести правильный пример грамматики, но можно попробовать примерно так 

// исправляем уродство #define, чтобы можно было писать многострочно
// также требуем типизации и декларируем возвращаемое значение.
#define* expr IF(boolean_expr condition,
                 operator if_true,
                 operator if_false = NULL) 
lisp.if(condition,if_true,if_false) // допустим, транслируем в лисп. 
#end*

Теперь мы можем писать не только if(){}else{}, а ещё и
IF(expr,{},{}). Ниже объяснено, зачем это. 

Допустим, у нас уже есть парсер, который возвращает дерево разбора в виде 
polymorphic_list(parser.if,condition,if_true_do)

или в виде 

polymorphic_list(parser.if_else,condition,if_true_do,if_else_do)

Тогда пишем:

PARSER_OUTPUT(parser.if,
  quasiquote(IF(unquote(DYNAMIC_CAST(expr,$$[1])),
                unquote(DYNAMIC_CAST(operator,$$[2]))
               )))

PARSER_OUTPUT(parser.if_else,
  quasiquote(IF(unquote(DYNAMIC_CAST(expr,$$[1])),
                unquote(DYNAMIC_CAST(operator,$$[2])),
                unquote(DYNAMIC_CAST(operator,$$[3]))
               )))

PARSER_OUTPUT - это макрос. Он задаёт элемент семантики компилятора.
Его действие состоит в том, что он добавляет куда-то в код компилятора правило преобразования AST, в корне которого находится
parser.if_else. А quasiquote - средство для построения кода по шаблону. Получается примерно такой компилятор:

// здесь где-то начинается огромный switch
switch (ast[0]) {
...
  // вот этот код сгенерится в итоге из нашего PARSER_OUTPUT
  case parser.if_else: 
    result=infix_expr(IF,{DYNAMIC_CAST(expr,ast[1]),
                          DYNAMIC_CAST(operator,ast[2]),
                          DYNAMIC_CAST(operator,ast[3])
                         }
                     );
    break;  
  // конец сгенерённого блока
...
}

Что в итоге? 
------------

В итоге у нас есть упрощённая префиксная форма IF, которая является основной. Она достаточно читаема: 

IF(x>0,a=1,{a=2;printf("уйх\n")}). 

Практически, это печатное представление AST. A if-else в его привычном виде становится хорошо изолированным синтаксическим сахаром. При создании метапрограммы мы можем использовать квазицитирование для построения префиксной формы IF и нам не нужно пытаться придумывать квазицитирование для обычной формы if-else, не надо париться с ";" перед else. Когда программа напечатает код, который она обработала (например, на выходе макропроцессора), он будет не так красив, как if-else, но его вполне можно будет прочесть (читают же лисперы лисп). В принципе, при желании можно написать и pretty-printer, который будет превращать IF обратно в if. 

Также здесь показано, как оформить DYNAMIC_CAST как макрос, без привлечения шаблонов С++. Это даёт упрощение языка. В реальном языке, конечно же, должны, по возможности, применяться правила неявного преобразования типов. 

За счёт квазицитирования, язык становится выразительнее. Можно явно указать момент выполнения вычислений. Это может быть момент компиляции или момент построения шаблона (уже в рантайме). Все эти идеи, в общем-то, представляют из себя кальку с дизайна лиспа. 




Теперь, насчёт операций. Их количетво в языке нужно поубавить, чтобы что-то осталось для пользователя. В лиспе операции можно перекрыть не только для типов (как родовые функции или с помощью typecase), но и для пространства имён, т.е., foo::+ и bar::+ будет означать разное. Это даёт дополнительную степень свободы по сравнению с С++. Например, можно переопределить + для целых чисел. Такой механизм переопределения ортогонален к системе типов и в этом его сила. 

> там (в Це) явно не хватает: а) замыканий. 
Мммм. В принципе, замыкания эмулируются структурой и полем-функцией в ней. Дальше уже вопрос ограничений синтаксиса. При нормальном макропроцессоре можно было бы сделать всё намного удобнее. Пример приводить нет сил. 

> квалификации структур, типа директивы using (или как там) в Паскале
В Паскале есть подства:

with foo,bar do
 baz;

если существует foo.baz и bar.baz, то можно ошибиться. SQL решает эту проблему лучше:

select baz from foo,bar 
/// ошибка компиляции

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

Т.е., 
program a uses foo,bar;
begin
quux; // определено только в foo - нормально
foo.baz // нормально. 
baz // ошибка компилятора,а в Паскале её нет. 

Для дополнительной гибкости вводим из лиспа symbol-macrolet. 
with baaz=foo.baz
  baaz; // заменяем вхождения baaz на foo.baz 
        // сразу после построения AST. 

В общем-то, это - то же, что 
#define BAAZ foo.baz, но в лексически ограниченной 
области видимости и с анализом конфликтов. 
with baaz=foo.baz,baaz=bar.baz - ошибка компилятора. 

В лиспе имеется недостаток дизайна: он выдаст ошибку в первой
строчке этой программы, т.е., в uses. Это - полное дерьмо, но не будем о дерьме. 

Что такое Дейкстровские конструкции?

den73 ★★★★★
()

>Сборку мусора - в мусорное ведро вместе с JIT. Всё это - разводка на бабло, чтобы покупали новое железо. Вместо этого безопасность дадут декларации работы с памятью: function foo(caption:string):fresh(widgets.form); Что-то мне подсказывает, что весьма простым способом можно с помощью таких деклараций и выводов из них построить работу с памятью без сборщика мусора и с высокой степенью автоматизации (хотя могу ошибаться).

Очевидно, юный падаван сам никогда не писал компиляторов, а уже лезет указывать старшим, как строить коммунизм. Ну ладно, Java при желании можно назвать ущербной. Но уж назвать Micrsoft с Хельсенбергом идиотами, которые не могли создать язык без JIT компиляции и сборки мусора, это надо определенно иметь мужество и смелость!

>11. Не ОО. функции, примитивные типы и структуры. И хватит. Вместо ОО - функции как объекты первого класса. ОО должно быть маленькой и лёгкой библиоткой (стиля JavaScript, наверное).

Туда же. Позвони Хельсбергу и расскажи, что ему нужно было разрабатывать C# без поддержки ОО. Любопытно, что ты услышишь из трубки в ответ

>14. Наверное,кроссплатформенность. Причём, в определении платформы должны содержаться и базовые элементы работы с GUI.

Ты действительно веришь в то что Java Swing под Windows Vista тормозит? А ты запускал GUI Java программы под Vista?

anonymous
()
Ответ на: комментарий от true_admin

И таки да. Все единое, одобренное Думой, Грызловым и Мироновым, все остальное суть ересь и неэффективное расходование денег, коих и так осталось очень мало. Конкуренция нам не нужна, Партия лучше знает, КАК надо.

anonymous
()
Ответ на: комментарий от anonymous

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

anonymous
()
Ответ на: комментарий от anonymous

> пошел нахуй, ебаный коммунист. или пиши доказательные аргументы вместо хуйни, почитатель Аристотеля.
anonymous (*) (31.01.2009 15:45:21)

а я есчо и каггатавлю

Valeriy_Onuchin ★★
()
Ответ на: комментарий от den73

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

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

anonymous
()
Ответ на: комментарий от anonymous

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

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


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

> > Замыкания... Наверное, состояние в них должно быть явным.

> Ты офонарел? Это ж сколько писанины будет

Это просто ты плохо владеешь телепатией. Имеется в виду, что оно должно быть доступно. Типа get_state_struct(closure).

> Наверное,кроссплатформенность.

Под кроссплатформенностью я имею в виду кросплатформенность в стиле HaXe, т.е., способность транслироваться в разные попсовые языки.

den73 ★★★★★
()
Ответ на: комментарий от den73

Хотя не факт, что даже в таком смысле чистоту проверить легко. Хотя кажется, что можно её хотя бы гарантировать за счёт производительности.

den73 ★★★★★
()
Ответ на: комментарий от den73

у вас большие проблемы с образованием, на самом деле.
Правильно все же вас в c.l.l. засмеяли.

anonymous
()
Ответ на: комментарий от Valeriy_Onuchin

Ничего не понял... :(

Васик -- плохой язык. Фортран -- плохой язык. Паскаль -- хороший язык. Алгол -- хороший язык. Це -- самый хороший язык. ЦеПП -- вообще не язык...

:-)

Die-Hard ★★★★★
()
Ответ на: комментарий от Die-Hard

А что, плохо читается? У меня нормально выглядит, перенос длинных строк работает (FF3 под оффтопиком). Какое поставить для твоего удобства? Впрочем, тема всё равно умерла.

den73 ★★★★★
()
Ответ на: комментарий от anonymous

> у вас большие проблемы с образованием, на самом деле. > Правильно все же вас в c.l.l. засмеяли.

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

den73 ★★★★★
()
Ответ на: комментарий от anonymous

Ммм. Спасибо. Я подумаю. По правде говоря, я не люблю Хаскель и учить его только для того, чтобы понять материалы, вряд ли стану.

den73 ★★★★★
()
Ответ на: комментарий от den73

Пожалуйста :) Со знанием ФП хаскель учится дня за два - и этих знаний хватит. Тем более, что он (или его синтаксис) используется во многих research papers, системах доказательств и других языках (та же Agda).

anonymous
()
Ответ на: комментарий от den73

> А что, плохо читается?

Вообще не читается -- длинные строки так и висят ...

Годовалый Огнелис -- мне нет охоты каждый месяц перепрывыкать к новым фенечкам, однако...

Die-Hard ★★★★★
()
Ответ на: комментарий от anonymous

> Word wrap extension не спасает, не?

Ну, не знаю...

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

Удачи в дальнейших извращениях! :-)

Die-Hard ★★★★★
()
Ответ на: комментарий от Die-Hard

Да, на втором огнелисе криво выглядит. По-моему, это скорее проблемы данного сайта, но ладно, учту. Я тоже консерватор :)

den73 ★★★★★
()
Ответ на: комментарий от den73

> По-моему, это скорее проблемы данного сайта, но ладно, учту.

Почему?

Преформаттед текст подразумеват, что пользователь увидит твой текст в точности таким же, как ты его написАл. ИМХО глубоко неправильным является использование всяческих новомодных свистелок, лучше пользователя знающих, что ему надо...

Die-Hard ★★★★★
()

Начать надо с того, что anonymous (*) (30.01.2009 0:56:45) криворукий тупень, идеально подходящий язык для которого - вижуал барсик.

rlisper
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.