Но ведь скомпилируется! А переопределить поведение в этом случае технически вполне возможно, только скорее всего никому не нужно.
Думаю, если в динамическом языке написать какую-нибудь фигню вроде инкремента сложного нечислового объекта, то тоже будет Runtime error.
Мы же говорим не о математике, а о программировании.
Но даже если и сравнить с числами — никакого более общего смысла в динамических языках нет. В статических языках появляются дополнительная возможность — типизация на этапе компиляции, которой нет в динамических. При этом в современных статических языках есть все возможности из динамических. То есть этой типизацией можно пользоваться, а можно и не пользоваться. То есть динамические имеют меньше возможностей по сравнению с современными статическими (хотя их правильнее наверное называть комбинированными, т.к. они сочетают и динамику и статику), и поэтому яляются подмножеством. Чистая теория множеств:)
Вполне логично. Динамические языки это просто подмножество статических. Во многих статических языках вполне могут существовать типы any (dynamic) для «истинно динамического типа данных» и variant для суммы типов из фиксированного набора (алгебраический тип данных).
Динамический язык — это по сути статический, в котором из типов есть только «any».
Эти defer'ы реализованы в языке D вместе с исключениями (поэтому в отличие от одного defer в Go, в D целых три ключевых слова — scope(exit), scope(success) и scope(failure) — для общего случая, выхода без исключения и выхода по исключению, что видимо и позаимствовано в Boost.ScopeExit и здесь). А сама концепция подозреваю что более древняя чем реализации Go, D и Boost, наверняка в каких-нибудь теоретических трудах есть.
Концептуально все верно (параметризованные типы, шаблоны как средства подстановки, макросы как средства кодогенерации), но синтаксически ничего не понятно. Даже сложно сформулировать конкретные претензии, наверное питонистам это все ближе для понимания…
Но когда я читал аналогичные статьи про Nemerle, понятно было все, хотя с некоторыми мелочами я там мог не согласиться — но вполне осознанно и мог обосновать свою точку зрения. Здесь — не могу.
Этот синтаксис кстати очень неплохой, в отличие от имени «result» по умолчанию. Потому что здесь имена возвращаемых значений объявляются пользователем явно и могут использоваться при расчетах как обычные переменные.
Спасибо! Всегда интересно пообсуждать языки программирования. Вот несколько вещей, которые на мой взгляд являются недостатками.
1. Синтаксис нечувствительный к регистру (все кроме первых символов), да еще и игнорирующий подчеркивания. Якобы для удобства интеграции с другими языками, а как по мне так наоборот неудобства и путаница.
2. Экспериментальная «фича» (т.е. ее нужно включать явно) — nim-lang.org/docs/manual.html#syntax-strong-spaces — приоритет пользовательских операторов зависит от количества пробелов перед ним.
3. Странный набор операторов для беззнаковых чисел: a +% b, a -% b и т.д.
4. Сама по себе концепция «пользовательских операторов» очень сомнительная и явно не добавляет читаемости.
Что интересно, дизайн и цветовые решения с каждой новой версией Windows все «холоднее». Начиная от теплых веселеньких цветов Win1 (желтый, зеленый, ораньжевый, толстые моноширинные шрифты) и заканчивая черно-фиолетовой десяткой, напоминающей о холодной глубине космоса…
А я думаю вот что.
Мне не нравятся исключения в существующем виде тем, что кроме вызова функции необходимо еще городить try-catch, причем если возвращаемое значение доступно из прототипа функции, то какие там функция выбросит исключения — тайна, для раскрытия которой нужно или читать документацию (если она есть и актуальна) или изучать код функции и всех функций, которые она вызывает. То есть — неявность.
С другой стороны, бесконечные if-else тоже не лучший вариант.
Откуда вывод — необходимо придумать что-то еще, совмещающее достоинства обоих методов и лишенное недостатков.
И вот какие мысли. Основной недостаток исключений — неявность. Поэтому сделаем их явными. Для начала, использование/неиспользование исключений в данном конкретном месте — это дело программиста. То есть если программист заключает функцию в try-catch с обработкой конкретного типа исключений — то этот тип исключений может генерироваться в данном коде. Иначе — не может, и должен генерироваться код возврата.
Хорошо бы в заголовке каждой функции обязать прописывать исключения, которые она в принципе может генерировать.
Принцип «ошибка это значение» тоже сохраняется. Для этого должен быть оператор, аналогичный return, но умеющий вместо возврата кода ошибки выбрасывать исключение, при его разрешенности в данном контексте вызова.
И наконец, переход от распространения исключения к возврату ошибки (хотя это самое сомнительное, но ладно — напишу): для этого, при отсутствии явных блоков try-catch, каждая функция является неявным блоком try-catch, превращающим любое исключение внутри себя в возврат кода ошибки по умолчанию для данной функции. Со всеми фичами исключений вроде раскрутки стека.
Выглядеть это должно так.
1. по умолчанию программист вызывает любую функцию, будучи уверенным, что она не выбросит исключений, а всегда вернет код возврата.
2. если программист хочет чтобы функция выбросила исключение и он готов его обработать, он просто разрешает раскрутку этого исключения для каждой функции, входящей в цепочку вызовов, до того места где исключение ловится; и второе — перед вызовом функции используется специальное ключевое слово (то же try), говорящее что мы готовы принять и исключение в том числе. Это наглядно, никаких неожиданностей — написано int x = try foo() значит мы не просто вызываем функцию, а «пытаемся вызвать, понимая что может и не получится». Городить catch при этом не нужно: если функция foo() вывалится с исключением, а у нас нет на нее catch — сработает catch по умолчанию, который конвертирует это в код возврата ошибки для данной функции. Исключение может распространяться дальше только в случае если мы разрешили это, указав в заголовке нашей функции что она может генерировать исключение такого типа.
Пока еще не во всем уверен до конца, но как-то так…
Выход из множества циклов одновременно решается введением в язык именованных блоков кода и операторов break (и continue заодно) с аргументом — именем блока. Имена блоков также выполняют роль «комментариев» и повышают читаемость программы. Поскольку я до сих пор не встречал практической реализации именованныз блоков, честно считаю это своим собственным изобретением:)
В целом не имею ничего против goto, хотя и не использую в коде (просто не сталкивался с необходимостью). Не имею ничего против потому, что несмотря на все доводы «против», есть один большой довод «за»: языки возможностей лучше языков ограничений. Мало-ли для каких хакерских целей может понадобиться профессиональному программисту этот оператор? Это не нам решать. Хуже когда что-то нужно, а нету. А значит, пусть будет.
Там скорее перевели заменами сишный код в синтаксис Ди, чем «переписали». Код-то по сути тот же самый, многие куски чисто в олдскульном goto-oriented стиле (видимо еще от Symantec осталось). И оригинальный backend не переписывали, а там самая жесть.
Думаю, если в динамическом языке написать какую-нибудь фигню вроде инкремента сложного нечислового объекта, то тоже будет Runtime error.
Но даже если и сравнить с числами — никакого более общего смысла в динамических языках нет. В статических языках появляются дополнительная возможность — типизация на этапе компиляции, которой нет в динамических. При этом в современных статических языках есть все возможности из динамических. То есть этой типизацией можно пользоваться, а можно и не пользоваться. То есть динамические имеют меньше возможностей по сравнению с современными статическими (хотя их правильнее наверное называть комбинированными, т.к. они сочетают и динамику и статику), и поэтому яляются подмножеством. Чистая теория множеств:)
То есть полиморфизм тут вообще не при чем.
Динамический язык — это по сути статический, в котором из типов есть только «any».
Но когда я читал аналогичные статьи про Nemerle, понятно было все, хотя с некоторыми мелочами я там мог не согласиться — но вполне осознанно и мог обосновать свою точку зрения. Здесь — не могу.
1. Синтаксис нечувствительный к регистру (все кроме первых символов), да еще и игнорирующий подчеркивания. Якобы для удобства интеграции с другими языками, а как по мне так наоборот неудобства и путаница.
2. Экспериментальная «фича» (т.е. ее нужно включать явно) — nim-lang.org/docs/manual.html#syntax-strong-spaces — приоритет пользовательских операторов зависит от количества пробелов перед ним.
3. Странный набор операторов для беззнаковых чисел: a +% b, a -% b и т.д.
4. Сама по себе концепция «пользовательских операторов» очень сомнительная и явно не добавляет читаемости.
Мне не нравятся исключения в существующем виде тем, что кроме вызова функции необходимо еще городить try-catch, причем если возвращаемое значение доступно из прототипа функции, то какие там функция выбросит исключения — тайна, для раскрытия которой нужно или читать документацию (если она есть и актуальна) или изучать код функции и всех функций, которые она вызывает. То есть — неявность.
С другой стороны, бесконечные if-else тоже не лучший вариант.
Откуда вывод — необходимо придумать что-то еще, совмещающее достоинства обоих методов и лишенное недостатков.
И вот какие мысли. Основной недостаток исключений — неявность. Поэтому сделаем их явными. Для начала, использование/неиспользование исключений в данном конкретном месте — это дело программиста. То есть если программист заключает функцию в try-catch с обработкой конкретного типа исключений — то этот тип исключений может генерироваться в данном коде. Иначе — не может, и должен генерироваться код возврата.
Хорошо бы в заголовке каждой функции обязать прописывать исключения, которые она в принципе может генерировать.
Принцип «ошибка это значение» тоже сохраняется. Для этого должен быть оператор, аналогичный return, но умеющий вместо возврата кода ошибки выбрасывать исключение, при его разрешенности в данном контексте вызова.
И наконец, переход от распространения исключения к возврату ошибки (хотя это самое сомнительное, но ладно — напишу): для этого, при отсутствии явных блоков try-catch, каждая функция является неявным блоком try-catch, превращающим любое исключение внутри себя в возврат кода ошибки по умолчанию для данной функции. Со всеми фичами исключений вроде раскрутки стека.
Выглядеть это должно так.
1. по умолчанию программист вызывает любую функцию, будучи уверенным, что она не выбросит исключений, а всегда вернет код возврата.
2. если программист хочет чтобы функция выбросила исключение и он готов его обработать, он просто разрешает раскрутку этого исключения для каждой функции, входящей в цепочку вызовов, до того места где исключение ловится; и второе — перед вызовом функции используется специальное ключевое слово (то же try), говорящее что мы готовы принять и исключение в том числе. Это наглядно, никаких неожиданностей — написано int x = try foo() значит мы не просто вызываем функцию, а «пытаемся вызвать, понимая что может и не получится». Городить catch при этом не нужно: если функция foo() вывалится с исключением, а у нас нет на нее catch — сработает catch по умолчанию, который конвертирует это в код возврата ошибки для данной функции. Исключение может распространяться дальше только в случае если мы разрешили это, указав в заголовке нашей функции что она может генерировать исключение такого типа.
Пока еще не во всем уверен до конца, но как-то так…
Мой вариант на примере rust (кстати впервые придумал такой синтаксис около 10 лет назад, тогда точно аналогов не было):
В целом не имею ничего против goto, хотя и не использую в коде (просто не сталкивался с необходимостью). Не имею ничего против потому, что несмотря на все доводы «против», есть один большой довод «за»: языки возможностей лучше языков ограничений. Мало-ли для каких хакерских целей может понадобиться профессиональному программисту этот оператор? Это не нам решать. Хуже когда что-то нужно, а нету. А значит, пусть будет.