Pull to refresh
3
0

Разработчик

Send message

Вы, наверное, понимаете, что в мире C++ с исключениями всё очень и очень неоднозначно. И что Гугл отказался от их использования в C++ по совсем другим причинам. Которые совсем никак не связаны с тем, что в Java проверяемые исключения.

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

А не будь язык успешен, то никто не стал бы даже думать о таких вещах.
Серьезно. Проверять ошибки вместо обработки исключений — это простейший способ бесполезно потратить кучу времени.

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

Более того, в большей части приложений перехватчики нужны в основном на границах модулей, что позволяет избежать написания огромной кучи кода. А, как известно, лучший вид кода — это отсутствующий код. Его не нужно поддерживать, там нет багов, он даже не требует времени на написание.

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

Наследование — это просто составление контракта между объектом-предком и объектом-наследником. Точно так же, как есть контракт с между этими объектами и кодом, эти объекты использующим.


Не касаясь теоритических проблем, скажу про практические:


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

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

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

А это все имеет смысл только в рамках языка.


Компилятор не может отвечать за внешний мир. Любое взаимодействие с внешним миром не является ответственностью компилятора. Компилятор может проверить правильность только того кода, который обрабатывается этим компилятором. Мы можем любой код подменить во время выполнения, и компилятор не может гараниторовать соблюдение контрактов в этом случае.


Максимум что может сделать копилятор — запретить использовать неправильный код в случае если компилятору известно о его неправильности.


Язык может предоставлять безопасные средства обращения с внешним миром, но это не является гаранитей соблюдения внешним миром контрактов.


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


… Либо не соглашается и делает что хочет, но ответственность лежит полностью на внешнем мир

Я в изначальном комментарии сказал, что вызовов по указателю нет. Любой непрямой вызов попадает под это условие.
Нам достаточно проверить ориентированный граф вызовов на ацикличность. Граф без петель (вызов функцией самой себя тривиально проверяется). Сложность этого алгоритма — O(M), где M — количеством ребер, которое ограничено количеством ребер в полном графе, т.е. N*(N-1). Т.е. общая сложность — O(N^2). Вот задача поиска ВСЕХ таких циклов уже другое дело.

Можно прикинуть (очень грубо). Пускай N — количество функций. Наибольшее количество уникальных вызовов (в смысле откуда и что) будет достигаться, если каждая функция будет вызывать каждую. Таким образом для проверки нам потребуется проверить не более N^2 вариантов. Соответственно сложность проверки ограничена O(N^2), что не так уж и много.


К тому же, фехтование верхом на стульях требует времени: https://xkcd.com/303/

Можно не запрещать. Достаточно отслеживать циклические вызовы компилятором (чтобы неявную рекурсию убрать) и запретить вызов по указателю (чтобы не проверять в рантайме). Если с первым еще можно смириться (любой рекурсивный алгоритм достаточно тривиально превращается в нерекурсивный), то второе уже серьезно ограничивает область применения такого языка.
Учитывая, что для обработки ошибок используются исключения и что в императивной парадигме не всякий код возвращает значение, то альтернатив особо нет. Можно было бы сделать специальный класс, который бы обозначал отсутствие возвращаемого значения (назовем его Void), который бы использовался во всех API в качестве возвращаемого значения. И получилось бы примерно так: Action превращается в Func, Action<...> в Func<..., Void>, Task в Task и так далее. Что-то особого улучшения не ощущается.
Начнем с переиспользования. Которое никак с наследованием не связано. И ФП тут тоже не серебрянная пуля. Функции точно также зависят от других функций, могут требовать странных типов, а джунгли волшебным образом не исчезают. Просто обычно есть средства для создания модулей, в которых эти джунгли изолированы. Проблема автора скорее напоминает проблему отсутствия модульности в C++. Очевидно, проблемы конкретного языка называть пролемами парадигмы нельзя.

Пример со сканером, принтером и копиром — это классика плохого дизайна и неудачных абстракций. Зададимся вопросом, является ли поведение сканера и принтера частями поведения копира. Ответ довольно очевиден: нет. Копир может и не уметь сканировать или печатать. Некоторая реализация копира может использовать сканер и принтер, но это детали реализации, которые скрыты инкапсуляцией. И да, инкапсуляция про отделение деталей реализации от интерфейса, а не про защиту от доступа. Что ж, неудачный дизайн — не проблема ООП, а проблема тех, кто его использует.

В общем, ждем статью «Прощай, функциональное программирование».
Я, скорее, говорил про часть, которая связана с целевыми атаками или с кражей интеллектуальной собственности компании. Это не только дороже, но и требует совсем другого подхода к персоналу. Часто основной ценностью компании может быть хорошее знакомство с «правильными» людьми, а может быть и многолетней историей продукта.
Нет, я просто намекаю, что потенциальная ответственность в этом случае будет такая же, а потенциальная прибыль — значительно меньше
Заметим, что сама по себе кража чего угодно, кроме денег, богаче не сделает. Украденное нужно кому-то сбыть. И при этом еще избежать последствий. Если хакер действует из меркантильных соображений, то он вряд ли станет выбирать свой целью какое-нибудь «ООО Вектор». Там, с высокой вероятностью, нет ничего, ради чего стоит рисковать.
Тогда было бы логичнее сделать отдельное выражение let (init) something, которое было бы семантически было бы эквивалентно { init; something }

Тогда можно было бы использовать его не только для if и while, а в любом подобном месте. Скажем, в примере с захватом мьютекса мне может и не надо ничего проверять, я просто хочу захватить мьютекс, выполнить какое-то действие, а потом мьютекс отпустить.
let (std::lock_guard<std::mutex> lock(m)) if (!container.empty()) { // do something }

let (std::lock_guard<std::mutex> lock(m)) { // do something }
Посимвольный произвольный доступ в UTF-8 идея довольно сложно реализуемая. Посимвольный последовательный доступ сделать относительно несложно. В задачах парсинга это как раз очень облегчает жизнь. Для реалистичной по скорости реализации произвольного доступа нужно либо использовать UTF-32, что бьет по памяти, либо UCS-2, но она не покрывает весь юникод.

По хорошему, строки и символы должны быть отделены от массивов, байтов и кодировок. И уже разработчик будет решать, что ему использовать. Нужны что-то более сложное — вот тебе полноценные строки, но за них придется платить памятью и скоростью. Хотя юникод и поддержка многих языков в любом случае не будет бесплатной.
Вам для парсинга нужен доступ по произвольному смещению? Если не секрет, то зачем?
Например того, что существуют многобайтные кодировки.

Information

Rating
Does not participate
Registered
Activity