В Rust для этого есть impl Trait и Box<dyn Trait>. Как раз для примера из коммента от 0xd34df00d, когда мы не знаем, что за сущность нам пришлют, но знаем, что она будет уметь некоторую нужную нам функциональность.
В случае достаточно компактного кода — не спорю. Сам писал небольшую строкодробилку на Ruby (извлекал нужные мне данные из LaTeX-овского документа), после того, как задолбался обходить различные краевые случаи в коде на C, и от динамической типизации не сильно страдал. И соглашусь, что в названном вами случае статика действительно может быть излишней. Если же код достаточно большой, а внешняя среда в него попадает через ограниченный интерфейс — вполне логично будет всё вне этого интерфейса типизировать статически, а его самого — валидировать для соответствия этим типам.
То, о чём пишете вы, — не динамическая, а слабая типизация. Динамическая — это когда можно написать let x = 1; processNumber(x); x = 'string'; processString(x); и не выворачивать себе мозг из-за того, что переменная x должна иметь разный тип в разных местах кода.
Не знаю насчёт «общего назначения», но, когда я увидел, как можно вывернуть наизнанку код на Ruby, вмонтированный в RPG Maker (обычный Ruby, только немного урезанный), мысли были в общем и целом схожие.
Это — одно проблемное место во всей кодовой базе. Объект, вышедший из-под JSON.parse<someInterface>, в дальнейшем обрабатывается с учётом всех статических типов. В то время как в динамически типизированном языке он так и останется "чем-то, определённым в рантайме".
С пришедшим извне json неизвестного формата? Сказал бы, что, напротив, совершенно нетипичная и нежелательная, если бы сам с ней сейчас не возился (в попытках единообразно обрабатывать данные из нескольких таблиц БД). Но в общем случае, это же совершенно нормальная практика — требовать от JSON-а соответствия опеределённой схеме и выбрасывать ошибку (контролируемо, конечно, не по NPE), если пришла какая-нибудь ерунда.
В мою бытность студентом (шесть лет назад) у нас был компьютерный класс. Ubuntu на терминалах, первый урок — как перейти в консоль и выйти из случайно запущенного vi, код пишется в mcedit… Первые эксперименты (примитивная fork-бомба, завесившая намертво терминал), первые куски кода, про которые никто не понимает, как это работает, и прочие радости юного кодера.
Одной из этих радостей был gcc, настроенный лично завкафедрой. -Werror — наш лучший друг, в этом я убедился ещё тогда; особенно, когда количество активных предупреждений начинает потихоньку переваливать за рамки очевидного.
Писали мы какую-то программу для работы с массивами. Уж не помню, что такое требовалось, но помню, что решил поместить индексы к массиву во второй такой же массив (типа double). Никакой арифметики с ними не производилось — только перестановки. В конце же, соответственно, требовалось взять элемент первого массива, индекс которого лежит в нужном месте второго массива. Казалось бы, дело простое, да?
arr[indexes[i]]
Ta-damm! Ошибка — за давностью лет уже и не вспомню, какая именно, но я, глядя на неё, решил попробовать явное приведение:
arr[(int)indexes[i]]
Фиг вам! Та же самая ошибка.
… В общем, конечная строка кода выглядела так:
arr[(int)(float)indexes[i]]
И удаление любого из двух явных приведений типа приводило к выбросу предупреждения (ну а в силу -Werror — к ошибке компиляции).
Явно преобразовывать между int и bool, говорите?..
P.S. А ещё я про этот случай периодически вспоминаю при работе с Rust. Со всеми этими
let int: i32;
let float: f32;
...
let double: f64 = (float as f64) * (int as f64);
Но здесь хотя бы понятна суть: весь язык устроен так, чтобы не давать выстрелить себе в ногу, обрезав результат под выходной тип.
"Не согласен — возражай. Возражаешь — предлагай". У Вас есть какие-нибудь более конкретные идеи, что в данном случае мог и должен был сделать npm, чтобы предотвратить проблему? А то, честно говоря, ощущение такое, что мы по большей части застряли на первом этапе: недовольства полно, а реальных альтернатив — не особо.
В первой задаче специально имена подбирались для получения нецензурной регулярки? :)
Спасибо, было интересно оценить такого рода "олимпиадные задачи". Лабиринт понравился особо: некоторое время назад я пилил личный проект (своего рода point-and-click-квест) на Electron + React, и само понимание, сколько всего можно сейчас сделать, по сути, на чистом DOM, разглядел, можно сказать, на практике.
Я ж надеюсь, Вы не про этот Zombie?..
В Rust для этого есть
impl Trait
иBox<dyn Trait>
. Как раз для примера из коммента от 0xd34df00d, когда мы не знаем, что за сущность нам пришлют, но знаем, что она будет уметь некоторую нужную нам функциональность.То, о чём пишете вы, — не динамическая, а слабая типизация. Динамическая — это когда можно написать
let x = 1; processNumber(x); x = 'string'; processString(x);
и не выворачивать себе мозг из-за того, что переменнаяx
должна иметь разный тип в разных местах кода.Это — одно проблемное место во всей кодовой базе. Объект, вышедший из-под
JSON.parse<someInterface>
, в дальнейшем обрабатывается с учётом всех статических типов. В то время как в динамически типизированном языке он так и останется "чем-то, определённым в рантайме".P.S. Минус не мой :)
Скажите это разработчикам на Rust — авторам Helix, к примеру :)
В мою бытность студентом (шесть лет назад) у нас был компьютерный класс. Ubuntu на терминалах, первый урок — как перейти в консоль и выйти из случайно запущенного vi, код пишется в mcedit… Первые эксперименты (примитивная fork-бомба, завесившая намертво терминал), первые куски кода, про которые никто не понимает, как это работает, и прочие радости юного кодера.
Одной из этих радостей был gcc, настроенный лично завкафедрой. -Werror — наш лучший друг, в этом я убедился ещё тогда; особенно, когда количество активных предупреждений начинает потихоньку переваливать за рамки очевидного.
Писали мы какую-то программу для работы с массивами. Уж не помню, что такое требовалось, но помню, что решил поместить индексы к массиву во второй такой же массив (типа double). Никакой арифметики с ними не производилось — только перестановки. В конце же, соответственно, требовалось взять элемент первого массива, индекс которого лежит в нужном месте второго массива. Казалось бы, дело простое, да?
Ta-damm! Ошибка — за давностью лет уже и не вспомню, какая именно, но я, глядя на неё, решил попробовать явное приведение:
Фиг вам! Та же самая ошибка.
… В общем, конечная строка кода выглядела так:
И удаление любого из двух явных приведений типа приводило к выбросу предупреждения (ну а в силу -Werror — к ошибке компиляции).
Явно преобразовывать между int и bool, говорите?..
P.S. А ещё я про этот случай периодически вспоминаю при работе с Rust. Со всеми этими
Но здесь хотя бы понятна суть: весь язык устроен так, чтобы не давать выстрелить себе в ногу, обрезав результат под выходной тип.
"Не согласен — возражай. Возражаешь — предлагай". У Вас есть какие-нибудь более конкретные идеи, что в данном случае мог и должен был сделать npm, чтобы предотвратить проблему? А то, честно говоря, ощущение такое, что мы по большей части застряли на первом этапе: недовольства полно, а реальных альтернатив — не особо.
Попросил бы немного развить мысль: в каком production-ready языке эти возможности, на ваш взгляд, хорошо реализованы?
А всё потому что нефиг обучать систему на одном языке, а использовать на другом :)
В первой задаче специально имена подбирались для получения нецензурной регулярки? :)
Спасибо, было интересно оценить такого рода "олимпиадные задачи". Лабиринт понравился особо: некоторое время назад я пилил личный проект (своего рода point-and-click-квест) на Electron + React, и само понимание, сколько всего можно сейчас сделать, по сути, на чистом DOM, разглядел, можно сказать, на практике.
… из Роскомнадзора.
Возьму на вооружение идею, спасибо (не как кадровик, а как писатель) :)
Если трактовать null как "неизвестно что" — вполне разумно звучит :)