Никогда не думал, что это случится со мной, но, похоже, я выгорел. А ещё мне стрёмно. Да, это ещё одна статья про выгорание.
Я тут на днях смотрел на свою RSS-читалку и заметил, что под тегом «C++» у меня где-то три сотни непрочитанных статей. Я не прочитал ни одной статьи по плюсам с прошлого лета, и мне офигенно. Я не написал ни строчки осмысленного кода на плюсах за последние три месяца, с тех пор, как распустили отдел, где я работал, и мне просто супер. Я позволил себе хотеть больше никогда не писать на плюсах, и у меня появились крылья.
Только стало страшно, потому что это давно уже стало куском моей самоидентификации. Я писал на плюсах лет 17, это почти две трети моей жизни, и как-то очень стрёмно всё это выкидывать. Всё моё сеньёрство-помидорство, львиная часть моего опыта — она там, в наступании на плюсограбли. Кто я без своего костюма?
Короче, да, я выгорел. И я не знаю, что делать дальше.
Я очень люблю плюсы. Помню, как схомячил учебник Стивена Праты за неделю поездок в школу в седьмом классе (ну хоть что-то хорошее от дороги в полтора часа в один конец). Помню, как читал распечатку туториала по ACE (помнит ли сейчас кто-то ACE?) на какой-то школьной экскурсии классе в девятом. Потом у меня появился КПК, куда я мог залить chm'ку Саттера и читать её на пьянке у друга на даче (куда меня почему-то больше не приглашали) классе в 11-м. Примерно тогда же я уже и начал зарабатывать первые программистские деньги — снова спасибо плюсам. Мои самые светлые воспоминания того периода — бессонные ночи и встречи рассвета под чтение того же Саттера, Мейерса, Александреску, каких-то статей на RSDN, под написание собственного кода. Я был наивным и не думал о будущем, но я ощущал, что оно у меня есть, и что я его кую вот прямо сейчас. Что все эти бессонные ночи и кодомарафоны на сутки и больше — это моё.
Я люблю то время и благодарен мирозданию за то, что оно у меня было.
Но теперь пришла усталость.
Просто надоело ходить по минам. Нет, не по минному полю, а именно по минам, потому что на минном поле есть участки без мин. 17 лет назад, когда я начинал, писать свой вектор было весело и прикольно, и я мог это сделать за полчаса, а сейчас мне нужно несколько дней и постоянная консультация со стандартом. Тварь я дрожащая или право имею? Получу UB тут или нет? А тут? А тут? Да что там вектор, мы не так давно с khim довольно долго обсуждали, как написать строку, и едва смогли определиться с тем, каким может (или должен) быть тип элементов строки, чтобы не было алиасинга.
Просто надоело смотреть на чужие культи. На моей позапрошлой работе они все были так молоды, вся жизнь впереди хорошие чуваки, и они хорошо знают своё дело — data science, статистику, предметную область, что угодно — но на плюсах они просто отстреливают себе ноги. Тяжело смотреть. И это бессмысленно. Там не нужно то, что плюсы могут дать, там не нужна высокая производительность (всё равно они пишут клей), не нужна близость к железу. К счастью, очередной пришедший тимлид это понял и перевёл всех на питон. Правда, тогда мне пришлось уйти, питон — не мой язык.
А, да, производительность — эту самую топовую производительность плюсы тоже не могут дать.
Сравнить два POD одним memcmp
? Нельзя.
Использовать строки, где внутри char*
, не думая об алиасинге? Нельзя.
Передавать unique_ptr
в функцию, рассчитывая, что оверхеда не будет, как говорят из каждого утюга? Нельзя.
Написать свой вектор для trivially copyable/constructible/etc types, который бы не рассчитывал на то, что компилятор вырежет часть необходимых по стандарту вещей? Нельзя.
Вообще писать современный код, пытаясь при этом быть идиоматичным, но не рассчитывая на то, что компилятор свернёт все эти слои шаблонных конструкций? Нельзя.
Нельзя. Нельзя. Нельзя. Спасибо, но рассчитывать на достаточно умный компилятор приятнее в других языках.
Что там ещё? Автовекторизация? Держи карман шире! Даже в простейших случаях уровня «пробежаться по строке и подсчитать количество вхождений символа» (ассоциации подкидывают название файла laba1.cpp
) рукописный ассемблер оказывается быстрее того, что генерирует gcc -O3 -march=native
с нестандартными расширениями и дёрганьем интринсиков (а что остаётся от плюсов с интринсиками? шедулинг регистров?). Незначительно, на 2-5%, но и я не спец в ассемблере на этом уровне. Без интринсиков и оставаясь исключительно в рамках стандарта? Тогда проигрыш в 2-3 раза. Ладно, это с кодом, про это я скоро напишу отдельную статью.
Даже выделить место под тривиальный тип и выmemcpy
ть туда набор байт — нельзя без дополнительных приседаний, лайфтайм так не начнётся. До C++20. В C++20 начнётся, спасибо, починили, но когнитивная нагрузка языка от этого только выросла.
Когнитивная нагрузка вообще только вырастает, даже когда какие-то вещи исправляют, потому что я же, мать его, специалист, я же должен знать, что починили, когда починили, и как было раньше. Да и C++ хорош поддержкой легаси, а это значит, что ты с этим легаси можешь встретиться. Например, в прошлом месяце ко мне обратился приятель с вопросом о том, как что-то сделать в C++03.
И эта когнитивная нагрузка — она не потому, что задачи того требуют. Это не внутренняя непосредственная сложность предметной области. Это просто, ну, так исторически сложилось.
Смотреть на трёхэтажные конструкции из костылей, кстати, тоже надоело. Было 20 способов инициализации, добавили uniform initialization syntax, стал 21 способ инициализации, но это всё ещё недостаточно uniform, поэтому в C++20 мы сделаем uniform из обычных круглых скобочек. Кстати, кто-нибудь сходу помнит правила выбора конструкторов с initializer list? Что-то там про неявные преобразования с потерей точности, но если значение известно статически, то…
Я постарался выработать правило — если мне сходу неочевидно, что код делает, если мне приходится вспоминать стандарт, если я чувствую неуверенность, то не надо так писать код. Поэтому у нас есть uniform initialization syntax, но я пишу std::vector<int> foo(n, val)
, без всяких фигурных скобочек, потому что я не хочу думать, как интерпретация зависит от определения n
и val
. И мне стыдно так писать, потому что все посоны на раёне пишут посты в блогах, где всё красиво, с фигурными скобочками, со всеми новыми финтами.
То есть, конечно, кто-то может сказать, что чего там эта неуверенность, надо просто код писать и оттачивать навык. Но я не хочу оттачивать навык интерпретации талмуда на полторы тысячи страниц. В других языках это не нужно, даже там, где монады и зигогистоморфные препроморфизмы. Да и 10 лет назад я вполне успешно писал продакшен-код за живые деньги на этих же плюсах, а опыта у меня было точно меньше. Значит, наверное, дело таки не только во мне.
Да и проблемы возникают не только у меня.
- Вот NeoCode написал классную статью про концепты в C++20.
requires C1<T::type> || C2<T::type>
не то же самое, чтоrequires (C1<T::type> || C2<T::type>)
— не прелесть ли? Ну это чтоб не расслабляться, видимо. (?°?°)?? ??? - Или вот чувак описывает, почему при переходе на gcc-10 сломался vim. Спойлер: потому что там UB, которым компилятор пока что не пользуется, да и то не факт. Починено вот так: «The workaround vim uses to avoid these failures is to disable buffer overflow checks from being emitted by using -D_FORTIFY_SOURCE=1 define». Это vim, да. Ъ-юникс-хакеры, трушнее некуда.
- Или вот ESR (если эти инициалы о чём-то говорят) пишет, что C кончается.
- Или вот веселуха, например.
- Или вот хороший перевод. И да, я видел код, который ломался из-за похожих вещей.
- Или на днях у чуваков из PVS Studio была статья, где в комментах подискутировали, когда можно использовать
memcpy
/memset
, а когда нельзя. Если разработчики статического анализатора ошибаются в таких довольно прямолинейных вещах, то кто и когда тогда вообще не ошибается? - Или вот, кстати,
memset
в плюсах таки пользоваться нельзя. Никогда. Лично мне потребовалось поковырять стандарт примерно час, чтобы убедить себя, что нельзя (как же так, я всегда думал, что можно!). Чуваки на канале про плюсы на фриноде сначала сказали, что можно, потом обсуждение свернулось в сторону «прост не используйmemset
и не думай)))». Потом, правда, ещё один чувак таки попытался разобраться в проблеме, и попытался доказать, чтоmemset
на плюсах использовать таки можно, потому что алиасинг (и что?). Но на практике, конечно, всем пофиг.
А сколько CVE и прочих уязвимостей и падений появилось из-за того, что где-то не так с указателем поступили, где-то проверка не там была, где-то… Чёрт, как жаль, что я не сохраняю эти вещи.
Короче, это была усталость.
Потом я понял, что мне страшно. У меня развился паралич.
Прошлым летом я устроился на совершенно шикарную работу, моими коллегами были умные, шарящие люди, ежедневно читающие блоги плюсистов и новые пропозалы в стандарт, обсуждающие на ланче что-то из программирования и каких-то новых фишек, а не то, как они провели выходные или какие планы на следующие. Для которых «так нельзя, это потенциальное UB» — не пустой звук, а повод потратить два дня на реализацию фичи вместо двух часов и искренне поблагодарить меня, что я обратил внимание. Но даже они делали ошибки. Я делал ошибки. Мы все делали ошибки. Они не знали то, что я считал базовыми знаниями. Я не знал то, что они считали базовыми знаниями. Никто ничего не знает. Если они не могут, то кто может?
Мне страшно. Мне хочется забиться в угол и плакать. Я не могу быть уверенным как минимум в половине строк, что я пишу. У меня есть чувство, что я строю фекалодендритные конструкции, а убеждение хотя бы самого себя в том, что написанное имеет смысл, занимает неоправданно много времени. Что бы я ни делал, в моём коде будут UB. Я ни на что не могу повлиять. Психологи говорят, что выученная беспомощность тут где-то рядом, так что написание кода на плюсах для психики не очень полезно.
Иронично, что больше всего уверенности у меня в наименее нужных в среднем продакшен-коде вещах — что-то там на темплейтах этакое намутить, на constexpr
вот. Может, это потому, что вся эта мета-ерунда куда ближе к так близкому сердцу принципу «если оно компилируется, то оно работает».
А, кстати о темплейтах. Рабочий проект, где каждый .cpp
-файл компилируется по 5-7 минут даже без оптимизаций? Время до первой диагностики компилятора в те же 5 минут? Пердёж компилятора на десятки мегабайт в случае ошибок? Да, я сохранял в файл и замерял ради интереса. Потребление памяти компилятором в 5-10 гигов на файл? Билдсервер с 32 ядрами и 64 гигами памяти, на котором нельзя запускать больше чем этак 8 параллельных потоков компиляции? Проект на несколько десятков kloc, собирающийся на ней полчаса? Получите, распишитесь.
И тулинг. Мне куда проще находить, на что у меня тратится память, в том же хаскеле, который, как известно, только для факториалов и годится. Системы сборки? Ха. Апгрейд компилятора для прода? Жди лет пять после релиза стандарта. Пакетный менеджер? Ха-ха. Reproducible builds? Ха-ха-ха. Все места, где я работал, на это либо вообще забивали, либо вкладывали какое-то совершенно неадекватное количество ресурсов. Я понимаю, почему так происходит, у этого всего есть абсолютно логичные и объективные причины, по-другому и выйти не могло, но я устал так жить.
Это был страх от языка, но я пропустил его через себя. Три месяца назад меня уволили, я пришёл домой, расслабился, вот уже три месяца пишу код для себя (не на плюсах!), сделал пару мелких проектов и начал один покрупнее, и мне хорошо.
Было хорошо.
Совсем недавно я поймал и осознал страх другого рода, куда более экзистенциальный.
Кто я такой? Я программист на плюсах. Я пишу код на плюсах две трети жизни. Это действительно стало огромной частью моей самоидентификации. Почему я претендую на синиора? Потому что я успел отстрелить себе миллион ног и натаскать свою нейросеть в черепушке определять, какая нога отстрелена на этот раз, по отсвету дульной вспышки. Потому что все интервью, касающиеся языка, я проходил, как тут говорят, with flying colors. Потому что я могу даже не готовиться к интервью, и всё равно его пройду. Потому что я могу показать особо интересные куски кода на гитхабе, и для меня скипнут как минимум телефонное интервью, а иногда скипали и техническое собеседование, разговаривая только о жизни или в лучшем случае о дизайне систем, релевантных для места работы.
А так придётся выкидывать весь этот опыт в корзину и начинать почти с нуля. Ну, да, у меня есть какое-то там системное мышление, я дизайнил компиляторы, я дизайнил распределённые системы с противоречивыми требованиями, это, наверное, тоже кусок синьористости, но если меня разбудить ночью и спросить «почему ты претендуешь на синьора», я отвечу «потому что сиплюсплюс». Ко мне обращаются коллеги, когда там какой-то адовый баг или что-то не работает или надо что-то сделать на темплейтах или хорошее код-ревью бы. Это давно уже так — помню, ещё где-то на третьем курсе, когда я отрабатывал пропуски по физкультуре заплывами в бассейне, ко мне подплыл какой-то чувак и спросил: «а это ты 0xd34df00d? у меня тут вопрос по boost…»
Это просто прорва времени и сил, которая выкидывается вникуда. Это непереносимый опыт (каламбур, ага).
Ну вот пойду я сейчас собеседоваться на чистого хаскелиста какого-нибудь, или вообще в ресёрч. Что я там скажу? На кого вообще претендовать? На каком основании? Что-то там типы что-то там алгебра что-то там математика. Но разве это сравнится по сложности со стандартом плюсов? Разве сравнятся мои тамошние навыки с моим опытом в плюсах? 17 лет плюсов против этак лет 8-10 хаскеля. Против этак лет трёх какой-то математики (примат в вузе не считается).
Кто я, если выкинуть плюсы?
И, может, выкидывать это всё — ошибка. Меня ждут минимум в двух очень хороших местах, одно из них неплохо смотрится в резюме, в обоих местах готовы платить какие-то баснословные деньги, но просто не могу больше этим заниматься. Кто-то наверняка скажет, что я с жиру бешусь, особенно сейчас, когда неопределённость на рынке труда повыше. Наверное, кто-то будет прав.
Короче, да, мне немного страшно, и я устал. Я немножко стал динозавром. Когда-то я непонимающе смотрел на людей, которые в гробу видали все новые вещи, а теперь я замечаю что-то похожее в себе. Я не нахожу в себе силы читать новые вещи о C++, и только о C++. За последние полгода я поковырял и неплохо проработал два новых для меня языка, я ежедневно либо читаю всякие матанокнижки либо решаю задачки, мой текущий проект — игрушечный proof-of-concept-язык, но меня тошнит при одной мысли о том, чтобы прочитать ещё один пропозал или даже ещё одну пережёванную статью-рерайт cppreference о каких-нибудь нововведениях в C++20.
Собственно, сначала я думал назвать эту статью «я стал динозавром», но проблема в другом. Те люди — они, кажется, вполне себе счастливы на своём C++03 или C99, или где там у них произошла кристаллизация, а я никак не счастлив и там. Проблема в том, что, получается, моему опыту, моим навыкам, вот этому всему — этому пора на свалку. А если этому пора на свалку, то не пора ли и мне?
И да, я ни в коем случае не хочу сказать, что плюсы — отстой. Вовсе нет, плюсы прекрасны, это я устал и кончился. Проблема не в тебе, проблема во мне.
jreznot
RUSTJavaScript уже советовали?0xd34df00d Автор
Зря зачеркнули.
Gorthauer87
Так и в чем проблема идти в Rust? Я ни секунду не жалею об этом выборе. Хотя до этого на плюсах писал лет 8. Но от перехода не было даже никакого экзистенциального кризиса, только пара месяцев ломки по некоторым паттернам.
Зато вот теперь смотрю на плюсы и ощущаю что то похожее на то, что ты описываешь, но уже смотрю со стороны на все это, это уже не часть меня больше.
0xd34df00d Автор
Rust — хороший язык. Но проблемы есть.
Хотя, конечно, раст наиболее близок к железу среди прочих языков. Но всегда можно заниматься компиляторами этих самых прочих языков.
Gorthauer87
Ну я прошел через этот путь, но не так уж это выкидывание страшно, это все равно что избавится от legacy, это на самом деле крутое ощущение, ты будто бы с нуля все делаешь и учишься.
Я вот в конце концов тоже из родного окружения C++/Qt ушел в неизвестность blockchain разработки. А сейчас еще и просто стало много вакансий в области системной разработки на расте, причем именно в штатах. В России то пока немного тухляк, увы.
0xd34df00d Автор
Зря я от одних идрисистов-блокчейнеров отказался с полгода назад, похоже.
Ft_iv
Как же я тебя понимаю. Я не программист. Но эмоциональное выгорание — это и моя тема на данный период. Когда вообще не хочется заниматься тем чем занимался и откровенно радуешься, когда занимаешься чем-то другим, не тем, во что вложил несколько лет образования и практической деятельности. И откровенно хочется заняться всем чем угодно, а не этой работой. И таких беспощадных усилий стоит чтобы просто заставить себя что-то сделать. Подсознательно ищешь поводы просто переключиться и не делать свою работу ) Обнимашки, короче, ты не один такой.
И какая разница какое время, главное ведь не страдать.
Ryppka
Мне кажется, это не эмоциональное выгорание. Это скорее объективно задолбало объективно задалбывающее. Несешь себе такой чемодан, вот и ручка оторвалась, и и чем дольше его прешь без ручки, тем меньше хочется переть и жальче бросить(
netch80
«Задолбало», если даже «объективно», это и есть эмоции. То, что они конструктивные и объективные, дело не меняет — автор чётко описал, что проблема именно эмоциональная.
И да, эти эмоции полезны — в плане изобретения нового и перехода на него.
PsyHaSTe
Я такого мужика в метро видел. Шел он такой, из последних сил тащил какой-то баул, и тут бах у него ручка отрывается, баул падает. Мужик сначала не понимает, потом не верит, потом краснеет, кричит на него "НУ И НЕСИ СЕБЯ САМ!!!", пинает его, разворачивается и уходит.
McKinseyBA
Возможно глупость спрошу, не судите — а Scala? Или Java-мир совсем не интересен?
0xd34df00d Автор
Ох, Scala лично для меня лежит где-то в uncanny valley на смешении функциональщины и императивщины. Под JVM я бы скорее на Eta писал.
PsyHaSTe
От многих людей слышал мнение, что скала фпшнее хаскелля. И завтипы, и частичные вычисления по крайней мере в ней реализованы, а в хаскелле пока что полу-фабрикатный LH есть.
0xd34df00d Автор
Там же не завтипы, а path-dependent types, разве нет? Пародий на завтипы у меня и в хаскеле достаточно.
Ну и у меня, если честно, от её[_] синтаксиса => [вытекают] глаза.
McKinseyBA
Про Haskell не могу сказать, но Scala одно время сам хотел освоить (в связке с Hadoop и Spark) и глаза не вытекали вроде. Вот R — действительно необычен синтаксисом. Хотя, конечно, все это вкусовщина)))
PsyHaSTe
Ну там можно выразить зависимую пару и зависимую функцию, значит завтипы есть :dunno:.
Что до синтаксиса, то тебе ли не знать что это вкусовщина к которой быстро привыкаешь.
Druu
Ни то ни другое нельзя.
PsyHaSTe
А стаковерфлоу говорит что можно.
Druu
То, что в интернете кому то что-то показалось — не аргумент, очевидно. Что суммы что произведения из примеров по ссылке — зависят от типов, а не от значений. Просто некоторые некомпетентные люди постоянно путают зависимость от типа-синглтона с зависимостью от значения.
PsyHaSTe
Можно конкретный пример на скале, и, скажем, идрисе, чтобы было видно, вот тут так можно, а вот тут так нельзя?
Druu
Нельзя, конечно. Чтобы построить такой пример, мне нужна конкретная формализованная кодировка, которая позволяет любой тип идриса преобразовать в тип на скале. Когда у меня эта кодировка есть — я тогда могу ее взять и показать, например, что это кодировка не покрывает все типы или покрывает некорректно (т.е. то что получается в результате не ведет себя как завтип). А такой кодировки никто не дал.
До того как такая кодировка предоставлена вопрос в принципе бессодержателен — зав. типов в скале нет, ну просто потому что не существует причин считать, что они там есть. На данный момент просто нечего обсуждать. А если ты попробуешь сделать такую кодировку и доказать ее корректность, то отсутствие завтипов в скале тебе станет полностью очевидно, если ты конечно не полный дебил и имеешь представление о предмете в целом.
А тебе, бро, лично совет — прекрати упарываться идрисами, матлогом и прочим и займись базой. Алгебра, топология, дифгем — нормально, с задачками, приложениями и т.п.
Потому что из-за отсутствия базовых знаний у тебя в голове вместо понимания остается дерьмо. Смотришь в книгу — видишь фигу.
PsyHaSTe
Геометрия мне со школы не нравится. Ну и я не очень понимаю, как оно тут поможет. Теоркат покушал — уже как бы забавно, но приложения мало. Идти дальше в какую-нибудь гомотопию — не, спасибо, пока не хочется.
По такому определению люди которые пытаются запилить в хаскель завтипы занимаются ерундой, ведь они как раз это и пытаются реализовать.
Druu
Тем, что без этого у тебя нет дисциплины, которая позволяет нормально валидировать т.н. "очевидные" утверждения. Когда тебе челиус пишет на стековерфлоу, что "вот у нас тут тип зависит от терма" — тебе в голову-то не пришло задуматься, от каких термов он зависит и от каких — должен зависеть, чтобы быть зависимым?
Хинт: на месте типового аргумента в случае зависимых типов может вставать любой терм (или, как минимум, это должно быть какое-то "интересное" подмножество — тогда конечно все равно полноценных зависимых типов не будет, но будет "интересный" фрагмент). Это значит, что если твоя "зависимость" выражается "path'eм", то этот "path" должен уметь кодировать любой терм скалы (либо "интересное" подмножество термов). А оно чо кодирует? Нихера не кодирует.
Это опуская другие проблемы.
Вообще, отличить зав. типы от их отсутствия просто. Вот ты можешь без зависимых типов написать Vec[N], ты можешь даже написать Vec[N] таской, что N нее будет известно в компилтайме. И потом для этого Vec[N] ты сможешь даже написать vecRef(vector, n) — и ты даже сможешь статически типизировать этот vecRef и тайпсейфно его применять в том случае, если у тебя известны n и длина vec, либо только длина vec, либо только длина n. Но вот чекнуть его в случае если неизвестно ни то ни другое — уже не выйдет, ты не сможешь написать refl.
PsyHaSTe
Мне кажется необходимость поднимать/опускать термы на уровень типов руками не делает язык завтиповым или нет, просто вопрос удобства.
Если ты покажешь пример на идрисе который по-твоему невозможно выразить на скале, я попробую представить контрпример.
Druu
Умение опускать или поднимать термы зависимым типам в принципе ортогонально. В зависимых типах никто термы не поднимает — ни руками, ни иным образом. В этом и смысл. Языком с зависимыми типами делает возможность написать функцию из терма в тип. Из любого терма — не только из константного. Например, этим термом может быть аргумент ф-и. Или результат применения некоторой ф-и к этому аргументу. Или выражение, содержащее локальную переменную. Пи-тип — это в принципе синтаксических сахар. На самом деле это просто обычная функция, которая записана для удобства оп особому.
Напиши любой ПИ-тип, который можно использовать как ПИ-тип.
PsyHaSTe
ниже пример
technic93
Как насчёт read_vec из гайда по индрису?
PsyHaSTe
Ну вот например: https://scastie.scala-lang.org/rPMW2bPpS2qLUt0sbHa3NQ
Druu
Эм, а при чем тут типы? Это метапрограммирование. Естественно, на макросах зав. типы делаются без проблем — ну просто можно в компайлтайме эмитить код на идрисе и его тайпчекать.
А типов-то там и нет. И сам код — не типобезопасный. Корректность обращения к массиву там не гарантирована.
И пи-типа ты тоже не показал ArrayFixed[A, N <: Int] на место N нельзя засунуть связанную переменную. Там останется просто Int. И тогда ты сможешь типобезопасно либо получить значение из массива с известной длиной, но с неизвестным индексом, либо с известным индексом — но с неизвестной длиной. В случае если и длина и индекс неизвестна — облом.
technic93
Ох, я не просил прям код, просто общую идею :) Но раз так, то я почитал и не понятно flatMap(_.get(2)) зачем? Почем не просто get(2)?
PsyHaSTe
Потому что вы создаете массив в рантайме, он может не создаться (для отрицательной длины в частности), поэтому функция парсинга массива
initDynamic
возвращаетOption[FixedArray[..]]
, ну а дальше через флатмап комбиинруем две функции возвращающиеOption
.technic93
А тьфу тупой вопрос был, чо то сбило с толку чтобы признать эту функцию. Я подумал в начале что это map по всему массиву.
А можно достать факт доказательства из get и носить его с собой пока он не понадобится опять?
PsyHaSTe
Ну там собственно с докзательствами можно всякое разное мутить. В отличие от идриса не получится чтобы термы и типы прям на одном уровне были, но в целом, по крайней мере на игрушечных примерах юзабельно. Я пока плотно скалой не занимался, поэтому подробности к сожалению никакие рассказать не смогу.
technic93
Мне не понятно какой тип у ArrayFixed.initDynamic(...), это каждый раз новый тип? Когда там в дженерик аргументах написано i.type, это как понимать?
PsyHaSTe
Ну если вывести сам массив на печать то получим:
Чтобы работать с ним тайпсейфно полагаю что его нужно проверить дополнительно на соответствие определенной длине, и тогда уже вернется
Some(конкретная_длина_которую_мы_ожидаем)
(или дипазон).Тут уже граница моего понимания скалы, особенно такой магической, поэтому дальше я, боюсь, ответить не смогу.
technic93
Ну эти рандомные числа это кишки из JVM наверное а пять оно походу просто печатает для красоты. А вот если попытаться саму скалу спросить то выдает типа такого
value wtf is not a member of Option[Main.ArrayFixed[Int, Int(otherLen)]]
Тут меня удивило то что в типе содержится название переменной. Не знаю как это работает, но может это и есть тот самый path dependent тип?
0xd34df00d Автор
Вспомнил это.
Вот, кстати, интересно, если зависимые типы я вроде как понимаю, а синглтоны со всеми этими костылями типа
SingI
или дефункционализации не понимаюи понимать не хочу, это хороший или плохой знак?PsyHaSTe
5 оно берёт потому что
val otherLen = "5".toInt
, это легко проверить. А вот как вытащить это на уровень типов — видимо матчингом каким-то, не знаю.Druu
С доказательствами там мутить нельзя ничего, т.к. тайпсейф код написать нельзя.
technic93
проиндексированный по значению i, а как если оно рантайм или по переменной i? типа что она otherLen, как выше писал Int(otherLen).
Druu
Оно формально по значению, но у вас нет возможности доказать равенство значений кроме как по is-a (с-но может быть ситуация когда значения одинаковые — а типы разные). В этом и проблема. Если бы было можно — были бы зависимые типы.
0xd34df00d Автор
На самом деле не совсем это, потому что синглтоны в хаскеле давно возможны — собственно, для этого достаточно
GADTs
. ДажеDataKinds
не нужны, это синтаксическое удобство (ну как индуктивные типы вместо чёрчеподобной кодировки через ?), синглтоноподобные кодировки формально ничего от неё не потеряют.Но да, среднее отношение к синглтонам — «ужас и костыли, пока не завезут нормальные завтипы».
А вообще на самом деле всё очень просто. Есть formation rule, который описывает, какие типы можно реализовывать в данной теории типов. Оно, упрощая, следует шаблону
А дальше всё зависит от того, какие
s?
иs?
допустимы (ну и как определяетсяs?
).Если и
s?
, иs?
могут быть только*
(то есть, типы), то получается базовое STLC. Можно показать, что П-байндер ни на что не влияет, и переписать тип в привычноеT? > T?
со стрелочкой.Если
s?
иs?
кроме того оба одновременно могут быть?
(где* : ?
), то получится ?_? (кажется, я всё время забываю формальные названия для этих частных случаев — короче, с типами, зависящими от типов и конструкторами типов).Если к этому добавить возможность
s?
быть?
, аs?
быть*
, то получится System F с конструкторами типов.А если разрешить все комбинации (в частности,
s? = *
,s? = ?
), то мы получим calculus of constructions, то есть, зависимые типы с полиморфизмом и конструкторами типов.Там везде при этом
s? = max(s?, s?)
, и там тоже есть варианты, но не суть.При этом всём
DataKinds
— это возможность создавать новые сорта с очень фиксированным количеством обитателей, не более.PsyHaSTe
Боюсь мой браузер не в состоянии отрендерить это правильно, у меня вместо части символов — квадраты.
0xd34df00d Автор
Лол, я не учёл, что символ для sorts очень похож на подстановочный символ для отсутствующего глифа. Короче, так и задумано.
PsyHaSTe
Век живи, век учись, никогда его не встречал.
Кстати, звездочки же забанили, теперь надо писать Type, правда?)
0xd34df00d Автор
В хаскеле — да (ну, на это есть экстеншн, по крайней мере, но я не знаю, приняли его уже или нет). Но это относительно стандартное теоретикотиповое обозначение, насколько я могу судить.
Просто в хаскеле есть значок * для умножения, а в теории типов нет никакого умножения на термах.
roman_kashitsyn
Мне раньше нравилась Scala. Бросил и возвращаться не хочу:
Bloat. Современный софт ужасен в этом плане, и Scala — явный тому пример. Я как-то чинил багу в парсере разметки в Lift Framework. Парсер жил в одном файле строк на 600. Компиляция этого файла занимала пару минут и порождала более 600 класс-файлов, т.е. более одного класса на строчку.
Очень тормозной компилятор, даже с fsc.
Побочные эффекты в неожиданных местах. Многие библиотеки выглядят чисто функциональными, но не являются потоко-безопасными, к примеру. Referential Transparency? Забудте.
Громоздкий синтаксис. Чтобы написать банальный Maybe нужно непростительно много букв и неочевидных фич языка.
Хороший язык должен снижать сложность, Scala эту сложность только преумножает.
Akon32
Это пофиксили в scala 2.12, теперь scala-лямбды компилируются в java-лямбды внутри класс-файла, а не в отдельные классы.
А чем стандартный Option не угодил?
PsyHaSTe
Есть подозрение, что вы со скалой очень давно общались. Компилятор достаточно быстрый (если не упороться шейплесом), библиотеки чистые (все эти tapir/rho/typed-schema + finch/http4s + IO/ZIO + doobie/quill + ...), синтаксис в третьей скале куда более читаемый имхо:
roman_kashitsyn
Последний раз я писал код на Scala в 2013 году.
Да, это выглядит лучше. В Scala 2.10 все операции реализовывались через переопределение методов трейта, что выглядело очень громоздко и требовало повторения сигнатур по 3 раза. И всё же Haskell заметно проще, компактнее и приятнее глазу:
В какой-то момент я осознал, что при написанию кода на Scala я на самом деле транслирую свои Haskell идеи в синтаксис Scala. Почему бы не писать сразу на Haskell?
PsyHaSTe
То есть очень давно, я угадал
Чтобы пользоваться благами жвм, в основном. Библиотеки/рантайм в который вложено просто в 1000 раз больше денег, крутче частичные вычисления,… Вот это всё. А в остальном это действительно про трансляцию хаскель кода на скала синтаксис.
Пока вам отвечал, узнал, что в скала3 появился облегчённый синтаксис:
Хуже ML, но лучше чем то, что было
0xd34df00d Автор
Можно же взять eta!
PsyHaSTe
Учитывая что я про неё только краем уха слышал (а еще слышал, что вне гхц жизни нет), то шансы что это действительно альтернатива очень малы.
0xd34df00d Автор
Я её сам не пробовал, но, как я понял, это что-то очень близкое к гхц, только темплейт хаскеля нет.
Хотя интересно, как они там без линзочек.
JuniorIL
Где же ее работу то на скале найти. Только в Берлине/Мюнхене/Тель Авиве.
PsyHaSTe
Да даже в москве хватает. Плюс, у нас в чатике ходит мем что любая java вакансия это секретная скала вакансия.
JuniorIL
Ну после кризиса будем посмотреть :-) Скалу очень люблю, но в моем случае Скала вакансия оказалась скрытой Джава тим-лид вакансией
middle
Это выкидывание контрпродуктивного опыта (опыта ходьбы по граблям). А продуктивный опыт — знание алгоритмов, умение писать быстрый код с учётом знания того, как работает процессор и память, и т.п. — это всё остаётся.
vsb
На мой взгляд это не выкидывание опыта. Rust молодой язык и явно не для начинающих. Думаю, подавляющее число программистов на нём как раз бывшие C++-ники. Т.е. вы будете вариться ровно в той же среде и собеседование будете проходить возможно на том же С++. Но чем больше вы ждёте, тем дальше эти группы будут расходиться и тем менее важным будет ваш опыт.
AnthonyMikh
Забавно, я слышал про Rust жалобы ровно противоположного характера: мол, среди
растомановпрограмистов на Rust дофига выходцев из Javascript, Python и Ruby.domix32
Вероятно человек не трогал раст со времен 1.0
blandger
И не только эти ЯП, а также Java!
Almet
Попробуйте программировать микроконтроллеры, опыт он и есть опыт
ne_kotin
Мне 40 лет.
Я 3 раза выкидывал свой опыт, начав в 2003-м с Delphi.
P.S. Я и сейчас иногда пишу на D7/FP если надо быстро накорябать что-то оконное и работающее под Windows и Linux. Но клиент как правило уже привык смотреть на результаты через браузер.
Acuna
Простите-с, а что такое «D7/FP»? Гугл меня не понял (вообще), однако иногда возникает жгучая необходимость «накорябать что-то оконное и работающее под Windows и Linux».
khim
Гугл в такое не умеет. D7 — это Delphi 7 (последняя версия до того, как какая-то муха укусила разработчиков и они ударились в .NET), FP — это FreePascal (хотя думаю, скорее, Lazarus).
Acuna
Ого, и правда не умеет, удивлен :) Спасибо, не знал что он кросс-платформенный. Возможно и стоит взять на заметочку, но блин, Делфи, даже как-то не знаю. У меня знакомый паскалист есть, 20 000 свои в месяц имеет, говорит больше уже просто не сделать, при этом занимается фотографией и у самого оборудования стоимостью с машину, просто запросы небольшие. Не знаю как он еще не сгорел, владея почти мертвым языком, наверное только благодаря фотографии и держится. Есть тут на Хабре, надеюсь не увидит мой коммент, а-то как-то неловко будет если честно :)
nmrulin
Ну сейчас пошло небольшое оживление. Весь старый код на Delphi теперь с небольшими улучшениями можно применить на iOS/Android. При этом среда разработки в разы шустрее, чем глючный Android Studio. Но всё равно он популярность Android Studio не обретёт.
Akon32
RAD Studio таки глючнее Android Studio. На больших проектах, например, автодополнение вешает IDE на десяток секунд, после чего ничего не выдаёт. В Idea поиск вариантов дополнения, видимо, делается в другом потоке, и его хотя бы отменить можно.
И плюс глючный компилятор, выдающий internal error без указания, где ошибка.
И, насколько я помню давние попытки товарищей написать что-то на делфи под андроид, hello world имел apk под 15МБ и запускался секунд 5.
Acuna
Ого, Делфи под Android? Это как это? Компилит код с С и запускает его через NDK, или как? Должны же быть библиотеки андройдоские, а они только сишные, ну под яву само собой.
safinaskar
> Это всё равно выкидывание опыта
Раз не хочется выкидывать опыт, и хочется всё же оставаться в C++, оставайтесь. Ездите на встречи комитета C++, заставьте их исправить недостатки C++, которые вы видите (можно через РГ21 пытаться stdcpp.ru ). Участвуйте в проектах (и пишите свои), которые активно используют новые фичи C++, которые фиксят недостатки старых. По поводу UB, возникающих из всяких memcpy: есть подвижки: www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4303.html, en.cppreference.com/w/cpp/utility/launder, wg21.link/P0476. Советую этот блог: blog.regehr.org, автор почти все свои посты посвящает своим попыткам сделать C и C++ точнее (и его компиляторы). Пишет разнообразные новые инструменты для этого. Например, blog.regehr.org/archives/1667, blog.regehr.org/archives/1619, blog.regehr.org/archives/1520, blog.regehr.org/archives/1393, blog.regehr.org/archives/1292, blog.regehr.org/archives/1128, blog.regehr.org/archives/1678, blog.regehr.org/archives/631. Используйте свои знания, чтобы сделать лучше альтернативы C++, такие как Rust. Или создайте свою
0xd34df00d Автор
Ну вот, кстати, писать тулинг было бы неплохим вложением ресурсов и опыта. Хоть в EDG иди,
правда, они на плюсах пишут.А с недостатками проблема в том, что я-то понимаю, откуда они берутся, они абсолютно логичны в рамках эволюции языка (примерно как возвратно-гортанный нерв или вывернутая сетчатка), но ввиду отношения к легаси (тоже вполне обоснованного) фиг их уже исправишь.
ZaMaZaN4iK
Кто б сомневался, что растоманы в тред набегут :))))
GreyGreyman
Это плохо?
ZaMaZaN4iK
Это смешно, скорее :)
PsyHaSTe
А ещё в тред набежали симаны, сиплюсманы, идрисманы, скаломаны и ещё кто только не набежал.
Мне кажется, у вас избирательная наблюдательность.
Revertis
Раст — самый любимый язык по опросам на стэковерфлоу за последние несколько лет, и оттуда он не уйдёт :)
puyol_dev2
Человек пишет, что не хочет программирование, просто не может об этом сказать прямо. А вы ему те же яйца только в профиль толкаете. Человеку программирование банально НА-ДО-Е-ЛО. Нужно заняться чем-то другим в жизни. Да, так бывает. Постичь программирование — это не вершина и не смысл жизни, просто один из многих видов деятельности человека
0xd34df00d Автор
Там ниже писали, что я что-то хорошо и выразительно написал — нет, вот нифига не хорошо и не выразительно.
Программирование я хочу, программирование я люблю, если бы я не хотел программирование, то момент увольнения в начале февраля не был бы так очевиден, в конце концов:
Просто конкретный инструмент надоел.
puyol_dev2
Другой инструмент — это шило на мыло. Через несколько месяцев, может пару лет, вы придете к тому же, что у вас сейчас
0xd34df00d Автор
Почему же?
Тем более, что некоторыми из других инструментов я тоже пользуюсь не год и не два.
khim
Они для вас не основные. В этом разница. Я когда-то давно был TeXником. Разбирался во всех этих стадиях работы TeXа и прочее. Там, конечно, не C++, но что-то похожее, со всеми этим стадиями работы этого чудо-юда.
Ощущение — было близко к тому, что у Вас по отношению к C++.
Ущёл, кстати, не потому что выгорел (до этого дойти не успел), а потому что за программирование пообещали больше платить, чем для разработку систем макросов.
Но теперь я и к C++ отношусь проще и понимаю, что если попытаюсь достичь вот самых-самых вершин мастерств… получу вот ту же самую боязнь и ненависть, что и в случае с TeXом.
0xd34df00d Автор
Ну как не основные… Дома все пет-проекты почти только на хаскеле, на последней работе хаскеля с плюсами было где-то пополам, на предпоследней были чисто хаскелевские проекты. Да, плюсы, наверное, «основнее», но это отношение скорее где-то 60:40, а не 95:5.
А теха мне хватило с точки зрения ковыряния чужих пакетов при написании статей.
netch80
> Так и в чем проблема идти в Rust?
Ну вот я очень плохо представляю себе, как писать в ООП без полноценного наследования реализации. Оно так или иначе вылазит в любом серьёзном проекте.
Можно, конечно, вспомнить, как это решалось на C. Но необходимость возиться с подобным пугает.
PsyHaSTe
Наследование реализации переоценено. Что на расте, что на хаскелле, пишем безо всякого наследования, и код только чище получается.
Да что там, у меня в сишарп проектах которые я последнее время пишу 0 (ноль) сеттеров у классов, и никакого наследования. И я бы не сказал что у меня там кривая архитектура получается или я там код не переиспользую. Стало только чище и проще.
anton19286
Композиция? А в шарпе для неё есть такой же сахар, как в го, когда компилятор сам ищет, к какому классу относится метод?
EvgeniiR
Лучше. В C# есть делегаты и явные интерфейсы.
Да и вообще голанг весьма бедный язык на «сахар».
khim
Ядро Linux — это несерьёзный проект, я так полагаю?
А это-то тут причём? GObject — он вообще о другом. Там поддержка скриптовых языков и рефлексия основную сложность создают.
А если просто писать, придерживаясь COI, то никаких проблем отсутствие «полноценного наследования реализации» не создаёт.
netch80
> Ядро Linux — это несерьёзный проект, я так полагаю?
Под «вылазит» я имел в виду «удобно применить».
И я по умолчанию считаю, что все случаи, когда наследование некорректно из-за заметно разного характера объектов (или из-за того, что их включается больше одного или переменное количество), уже исключены. А вот если надо (повторю соседний пример) из 100 методов переопределить только 5 — тут-то и начинается.
Linux характерен тем, что в нём таки легко уйти от подобных желаний — суть объектов заметно разная. Но, например, в сокетах уже есть очень заметное сходство, и то, что разделение сделано включением реализации в общий объект — вопрос стиля, а не жёсткой необходимости.
Или вот n_tty_ioctl_helper, если не нашёл свой случай, вызывает tty_mode_ioctl с теми же аргументами — почему нельзя это считать аналогом перекрытой виртуальной функции?
> А если просто писать, придерживаясь COI, то никаких проблем отсутствие «полноценного наследования реализации» не создаёт.
Создаёт, когда надо в отдельных реализациях менять только малую часть функциональности.
В моём основном проекте сейчас, навскидку, 3 таких иерархии — гарантированно (альтернатива — массовое тупое дублирование), и ещё 2 — немного лучше выглядят с наследованием, чем со включением.
> Я сейчас глянут — у нас в проекте, несмотря на использование C++ и много всяких интересных вещей, нет ни одного класса, который бы наследовал откуда-то реализацию.
Уровень языка тут к вопросу не относится. А вот специфика проекта — да, в полный рост.
khim
Да, это позволяет получить что-то работающее в очень малом количестве кода… но в современном мире — это очевидный антипаттерн и так делать не нужно. Количество глюков, которые возникают в разных программах из-за того, что никому неизвестно когда какие методы используются и какие «тонкие зависимости» между ними (перекрывая метод A, не забудьте перекрыть также методы B, C, и D… причём все эти описания хорошо если в документации, а не в коде)… вы это всерьёз сейчас предлагаете в язык, построенный вокруг гарантий безопасности тащить?
Нет, это не вопрос стиля. Это вопрос безопасности. Если вы перекрыли один метод в объекте, где их 100… то вы понятия не имеете, какие методы станут работать по другому. В принципе. Единственный подход, который что-то гарантирует — изучить подробно что написано во всех этих 100 методах. Но так никто не делает.
Можно считать. Но это не является наследованием реализации, потому что отсутствует основная «фишка» наследования реализации: возможность меняя поведение функции A «получить в нагрузку» изменение поведения другой, никак визуально не связанной с первой в точке перекрытия, функции B.
Ну да. Выглядит красиво. Иногда. Но… вы можете выбирать — выстрелить себе в руку или в ногу. А попадание в голову — идёт бонусом бесплатным.
Нафиг-нафиг. Нечего такого инструменту в безопасном языке делать.
Вот вообще ни разу. Если у вас в каком-то классе образовалось больше 10-20 виртуальных методов — то это чёткий признак того, то вы что-то сделали не так. И вам нужно или поделить этот объект на части, либо сделать часть методов невиртуальными, а скорее всего — и то и другое: сделать небольшое чисто виртуальных методов в одном объекте и кучу невиртуальных — в другом.
Появление такой каши — это явный признак того, что вы делаете непонятно что и у вас просто нет точного понимания чего вы хотите получить в результате, вот и всё.
0xd34df00d Автор
Так и для этого наследование реализации в смысле ООП не нужно.
В простейшем базовом случае у вас будет запись (совсем как структура в С), где перечислены какие-то функции. Хочется отнаследоваться, переопределить пять из них и добавить три — в простейшем базовом случае включаете имеющуюся запись в вашу, добавляете в вашу три новых функции, переопределяете пять нужных вам во включённой. В небазовом случае, если охота повыпендриваться, начинаете применять паттерны вроде trees that grow.
khim
Нет основной фичи: возможности при попытке выстрелить в ногу изящно отстрелить руку и остаться без головы.
Как в вашем подходе перекрытие одной из функций изменит, незаметно для вас, поведение другой функции?
Ryppka
Наследование реализации полезно в строго очерченной ситуации: а)безусловно имеется отношение IS-A и б)есть нетривиальный объем общего для иерархии наследования поведения. Классика — оконный интерфейс.
Когда а или б не выполняются, то наследованию реализации есть лучшие альтернативы.
khim
Наслоедование реализации — это костыль. Который позволяет иногда написать чуть меньше кода за счёт разкого уменьшения надёжости этого кода.
Неудивитеьно, что в языке «помешанном» на безопасности его не поддерживают.
Это всё равно как жаловаться на то, что в веганском кафе с выбором стейков очень плохо…
Ryppka
Особые конструкции в TP оставим на совести Филипа Кана, они к делу не относятся.
По сути, Вы передергиваете и, простите, впадаете в дискуссионный раж. Или Вы не понимаете, что куча макросов и наворотов в MFC и, прости господи, Qt служат — это особенности подкласса для _той самой унаследовванной реализации_, которая связывает код на C++ с C-вселенной GUI? В ATL/WTL это, кстати, уже сделано на CRTP и выглядит существенно приличнее. Может я и погорячился с однозначно высказанным «полезно*, но то что это *удобно* доказывает тот факт, что ООП, понимаемое как программирование с помощью ключевых слов class, virtual и override получило такое широкое распространение. Если что, я писал оконные процедуры на C, потом писал их с помощью windowsext.h и хорошо помню это наследование „вручную“.
netch80
> есть нетривиальный объем общего для иерархии наследования поведения
Ну вот я почему-то наблюдаю такие ситуации сильно чаще, чем обратные — которые и так по умолчанию решаются включением.
Страуструп когда-то сказал «Наследовать ли самолёт от двигателя? А будет ли у вашего самолёта два двигателя?»
Меня этот вопрос заботит в другом варианте: есть ситуация, когда уже есть нечто, грубо говоря, со 100 методами, а более специфичные реализации заменяют поведение только в малой части аспектов (пусть это будет 5 методов). В текущем проекте есть несколько таких мест. Переписывать их всех на реализацию остальных 95 (опять же условно, но пусть будут такие цифры) пробросом во включённый объект… честно не хочется.
И да, это не оконный интерфейс, это сильно другая задача — хотя последствия сходны.
Ryppka
Видимо, такая у Вас предметная область. А в другой могло бы быть совсем иначе.
Споры не имеют смысла.
В целом, главная слабость объектно-ориентированного подхода в том, что нужно выделить правильные сущности в предметной области, а это трудно. И требовать такого от прогера-Васи бессмысленно. Хрестоматийный пример о трудности реализации метода Fly у класса Penguin связан с тем, что критерии отнесения объекта к классу Bird не имеют ничего общего с возможностью полета.
technic93
А нельзя ли пробросить не 95 а наоборот 5? Или эти 5 отличий из 100 каждый раз разные.
netch80
В том и дело, что разные.
khim
Тот факт, что у вас вообще в одном мест собралось 100, потенциально перекрываемых, методов — это плохой признак. Очень плохой. Это значит что вы плохо понимаете что делаете.
khim
Если же у вас эти 100 методов «перемешаны» и активно вызывают друг друга…
Давайте я сейчас напишу тезис, а вы над ним подумаете. Вот только реально крепко, без «да ну, что это за фигня».
Наследование реализации — это ещё один способ трансформации классического спагетти-кода в нечто, что выглядит как структурный код.
И отсюда, собственно — всё и проистекает. И желание иметь этот инструмент (ведь структурировать программу сложно и долго, давайте лучше устроим кашу, когда никто не может сказать какой кусок кода вызывает какой-нибудь другой кусок кода и зачем), так и нежеление его предоставлять в современных языках.
Спагетти-код возникает всегда, когда вы недостаточно чётко представляете себе задачу, которую решаете. Это от типа исходной задачи не зависит.
Psychiatrist
Как поциент-поциенту. С++ тебя не отпустит, бежать куда-то с него бесполезно. Он тебя везде найдет. Всё, что ты можешь сделать — это создать «лучший С++», победив тем самым его в самом себе. Примерно так. Извини, что «на ты». На брудершафт не пили, знаю.
5oclock
У нас контора с плюсов расползается: половина на питон, другая половина — на js.