Pull to refresh

Comments 1975

UFO landed and left these words here

А вы посмотрите на остальные статьи этого эпатажного автора. Вы думаете, он опомнится на 27-ой разжигающей статье?)

Мне особенно нравится трезвая оценка с опозданием —
Пару лет назад я уже писал о типизации, но тогда я был молодой, глупый и трусливый

Остается немного подождать для оценки уже этой статьи.
UFO landed and left these words here

Афигенно! Автор, жги! * обновляет страницу, дабы насладится холиваром *

Холивар, вроде не пятница.
Динамическую типизацию зачем то придумал и мало того она жива до сих пор, обычно то что никому не нужно умирает на задворках истории.
Но удивительное дело в динамических появляются типы, а в типизированных val.
Такие дела.
Истина где-то рядом, наверно по середине.
И наверно не всех надо по одну гребенку.

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

val это не динамическая типизация, а лишь вывод типов. Вот почему то многие не понимают принципиальной разницы

Думаю просто смешиваются в голове все варианты: статическая/динамическая, сильная/слабая, явная/неявная.
Динамическую типизацию зачем то придумал и мало того она жива до сих пор, обычно то что никому не нужно умирает на задворках истории.

Как говорил один мой знакомый:


Так ведь значит же. Динамика это просто атавизм из 90-х, когда языки с нормальными системами типов делать не умели, а писать на вербозном говне не хотелось.

А ЯП с нормальными системами типов начали появляться относительно недавно.

А можете привести примеры ЯП с нормальными системами типов?

Ни в коем случае не троллинг, действительно интересно.

Haskell (хотя 0xd34df00d щас опять будет ворчать, что выразительности не хватает), Idris, вроде бы Scala (хотя точно сказать не могу), с некоторой натяжкой — Rust.

Странно (про Haskell), там ведь и Хиндли-Милнер, и алгебраические типы…
UFO landed and left these words here

Первые два скоро подвезут. Только-только появилось понимание как это сделать чтобы потом компилятор не треснул.
Третье доступно в виде библиотек.

UFO landed and left these words here

Мне ещё регулярно (в чужом коде) встречается Vinyl. Но конечно это всё полимеры...

  1. Динамика это просто атавизм из 90-х, когда языки с нормальными системами типов делать не умели
  2. Haskell, 1990 год.
  3. Python, 1991 год.

Как эти три вещи могут одновременно укладываться в голове? Видимо ваш знакомый не знает одной из них.


Не получится убедить, что Haskell — это подходящий язык для разработки, а Python — не подходящий.


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

Все таки Haskell это полигон для экспериментов, который тем не менее дорос до прода, а активно вывод типов начал проникать в индустрию только в 10ых годах.

Ну так хаскель 90-го года и хаскель совеременный — это очень разные языки.

UFO landed and left these words here
Лучшее, что может быть — это типизация по требованию. Когда нужно, беру и использую. Когда не нужно — избегаю кучи бойлерплейта.

Если бы она ещё работала… Потому что когда тебе нужно, а в апстриме не нужно — вылезай, приехали.

Но ведь в статике бойлерплэйта, как правило, меньше. Можно одновременно получить и безопасность, и читаемость.

Как правило больше, на мой взгляд. По крайней мере если брать языки типа C++, C#, Java, TypeScript

Ну вот и не надо их брать.

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

UFO landed and left these words here

Не очень из-за дурацкого деления типов на структуры и классы.

Структуры и классы никак не делят типы и не мешают. Это лишь определяет reference type/value type и системе типов до этого нет никакого дела.

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

В C# тоже самое. По факту, структуры придуманы не от хорошей жизни, а ради перфоманса. И мутабельные структуры — это дорога в ад (во всяком случае в C#). А если структура не мутирует, то думать про копирование особо не надо.

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

За мутабельные структуры компилятор всегда подскажет. Если один раз понять как работают reference type/value type в свифте и использовать их где нужно и как нужно, то никаких проблем не будет возникать, а компилятор в случае чего все-равно заботливо предостережет. Все четко и явно в этом плане. И не придется переживать за глубокое/поверхностное копирование.
А что не так с дженериками?
В самом простом варианте все по-умолчанию imutable, потому что компилятор не будет знать что именно туда придет, а для мутабельности можно и inout или var в нужном месте указать.
В случаях посложнее (generic constraints) у вас в протоколе все ограничения описываются, вплоть до указания что этот протокол только для классов.
Не знаю с какими проблемами вы сталкивались, но по этому поводу у меня голова ни разу не болела.

К сожалению он существует только в яблочной экосистеме.

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

Только пользователей пк на винде большинство.

87% это в целом по миру или в сфере разработки? Я видел винду только у тех разработчиков, которым по каким-то причинам было лень ставить линукс. Возможно страх перед неизведанным.

Страх перед паршивыми гуями тогда уж.

На самом деле почти у всех знакомых мне разработчиков в экосистеме .NET и 1С винда — основная ось для этой разработки. Линуксы — только для кроссплатформенных задач.

На самом деле у 100% разрабочтиков в экосистеме Яббле стоят XCode и MacOS — основная ось для этой разработки. Винды — только для задач HR/серкретарш и бухгалтерии.

UFO landed and left these words here

Так исторически сложилось, что на свифт перешли все кто писал на ObjC, а он существовал в рамках эппловских операционок, поэтому большинство пишущих на нем — маководы. А так как язык молодой, то пока еще не успел выбиться из нативной разработки под MacOS/iOS (в плане популярности), хоть эппл и делает многое, чтобы он мог быть универсальным. Бекенды эти ваши давно уже можно писать, с ардуинками играться, TensorFlow переходит на него как на основной язык. Дайте малышу время)

UFO landed and left these words here
хоть эппл и делает многое, чтобы он мог быть универсальным.
А что именно он делает? Мне просто интересно. Компилятор предоставил? Так Objective C всегда был под разные платформы (стараниями Столлмана, правда, вопреке желанию Джобса… но был).

Каких-либо попыток сделать разумную среду, которую можно использовать вне экосистемы Apple я не наблюдаю… да неясно какой в ней мог бы быть смысл: Apple же нужно сделать так, всё-таки, чтобы «хомячки» не разбежались с его платформы, а не чтобы кто-то вне её творил…

TensorFlow переходит на него как на основной язык
Кто сказал? Откуда уверенность, что из этого не получится очередная стелла на известном сайте?
Fullstack-разработка на swift вполне себе цель. В смысле — клиент под ios + серверсайд под линукс.

Про TensorFlow Google сказал, мол уходят с питона на свифт. Потому что быстрый, безопасный и: https://en.wikipedia.org/wiki/Differentiable_programming


Если появится очередная стелла, свифт от этого никак не пострадает. Но это не отменяет факта, что свифт уже не только язык для “хомячков” с платформ Apple.

Про TensorFlow Google сказал, мол уходят с питона на свифт
Где, когда, а главное, кто? Те, кто его разработал? Там им свою разработку и внутри Гугла надо как-то продавать — ещё бы они не излучали оптимизм.

Если появится очередная стелла, свифт от этого никак не пострадает.
Пострадает, конечно. Причём уже похоже, что не «если», а «когда». Итересно только — релиз успеют сделать или прямо из беты в небытиё?

Но это не отменяет факта, что свифт уже не только язык для “хомячков” с платформ Apple.
Та же самая история, что и с Objective C, на самом деле: когда Objective C только появился — народ разработал GNUstep и были даже попытки куда-то это всё приспособить. Однако со временем всё заглохло и, насколько я знаю, Cocoa уже никто никуда портировать особо не пытался — так, кой-какие обрезки для игрушек.

То же самое и здесь: каждая неудача применить Swift куда-нибудь, кроме iOS и macOS будет подчёркивать «неразрывную связь»: Swift == Apple, Apple == Swift.

Слабо себе понимаю причину захоронения S4TF. Ребята из Google просто искали наиболее подходящий язык и выбрали Swift. Cделали форк языка и на его основе допиливают под нужды. В Colab уже добавили. FastAI, курсы начали переводить. Единственная проблема, крайне сыроват еще, но светлое будущее :).

По-моему вы сами всё прекрасно описали:
Ребята из Google просто искали наиболее подходящий язык и выбрали Swift.
Именно так: не «Google искал», а «ребята из Google искали».

В Colab уже добавили. FastAI, курсы начали переводить. Единственная проблема, крайне сыроват еще, но светлое будущее :).
Где-то я это уже слышал… Chrome Apps, NaCl… Да собственно половина проектов из Google Graveyard когда-то были «сыроватыми, но со светлым будущим».

Слабо себе понимаю причину захоронения S4TF.
То же самое, что и всегда: не оправдал надежд, не набрал критической массы… Посмотрим. Самый важный вопрос не в том, смогут ли они в Colab что-то добавить, а смогут ли они хотя бы один «большой» проект этим увлечь… и то может не помочь: NaCl использовался в App Engine, но ему это не очень помогло…

Вот это самое "кроме" такой немаленький минус. И подозреваю в обозримом будущем оно не войдет в Tier1 поддерживаемых ОС. Rust вполне неплохая альтернатива в данной ситуации.

Тогда уж сразу AssemblyScript, это ts без js.

Есть язык D. Благодаря удобно сделанным шаблонам, утиной типизации, возможности ограничивать типы в шаблонных параметрах (часто используются обобщения для структур данных, например, чтобы условный тип массива и условный тип списка могли приниматься функцией поиска). Ну и плюс обычные Си-подобные типы данных и С++-подобное ООП (но без лютого трэшака). Нормальные массивы, хранящие и указатель на себя, и свой размер. Там много хорошего, выводящего на другой уровень программирование на Си-подобных языках. Как раз тот язык, благодаря которому никак мне не удаётся полюбить динамическую типизацию, хоть я и много времени программирую на Python'е, JavaScript'е и bash'е по долгу службы.
Динамическая типизация переносит ряд возможных ошибок на время исполнения программы вместо времени компиляции.
На самом деле очень прокачивает скиллы кардинальная смена стека. Я всю жизнь (лет 15 я думаю) писал на PHP, пару лет назад понадобилось прочно влезть в яву (более строго-типизированного языка я в жизни не видел), дак я вам скажу что именно после того, как я вернулся обратно на PHP я полностью оценил все преимущества статической типизации, ибо мне не нужно боятся что по какой-то причине я загоню аргументом строку, хотя должен был массив (например, изменил возвращаемое значение какой-то другой функции), представляете что произойдет при этом, особенно когда эта функция должна будет пройти этот массив и сделать несколько запросов в БД или сконвертить его в JSON и отправить его на сервер? Теперь начиная с 7 версии PHP тоже имеет строгую типизацию и заругается если аргумент имеет другой тип данных или функция возвращает другое значение, а не то, которое от нее ожидается.

Как по мне оптимальна гибридная типизация, ибо иногда просто хочется расслабиться и что-то наклепать на коленке, не задумываясь о типах, но в серьезных проектах на том же PHP строгая типизация просто необходима по причинам, которые я описал выше. Причем я понял что словами это не объяснить, с этим нужно сталкиваться чтобы оценить все преимущества.

P. S. Это еще ладно, я еще и после этого с MySQL на PostgreSQL перешел (который тоже строготипизирован), теперь он меня обругивает каждый раз если по какой-то причине в строку суется число (а это может быть следствием какой-то очень серьезной проблемы, ибо почему возвращается число там, где должна возвратиться строка, например, array_search не нашел какое-то значение в массиве, хотя должно, что означает что этот массив сформирован неверно). Очень сильно выручало уже, хотя я не так давно пользуюсь всеми ее преимуществами.
писал на PHP, пару лет назад понадобилось прочно влезть в яву (более строго-типизированного языка я в жизни не видел)
Это не та ли система типов, которая считает null объектом любого типа?
В РНР эту «особенность» умудрились не повторить, кстати.

Наверное, просто потому что null в PHP появился чуть ли не раньше чем сама Java появилась (шутка, она старше на пару месяцев) и изначально был отдельным скалярным типом, когда объектов ещё даже в проекте не было

это круто, конечно, что умудрились не повторить ошибку системы типов, которую проектировали четверть века назад. Дженерики тоже сразу сделали, а не как в джаве?
Ну да, всю систему типов на помойку, и сам язык туда же, у него же тип null есть!
Типа null у него как раз нет. В этом и проблема.

Ну так-то вот эта концепция с null самим её изобретателем признана "ошибкой на миллиард долларов".

заругается если аргумент имеет другой тип данных

Нуда, только его ругание попробуй еще перехвати, приходится статический анализатор гонять

ЗЫ на самом деле Php начинает нервировать, ятоже много лет на нем пишу, и у меня все более отчетливое желание писать на jsp или на чистой Java

TypeError обычное исключение. Обычно его и особо перехватывать не нужно, так же как любое необработанное.

Как правила это — ошибка в коде и ловить такие вещи в рантайме — странная идея.

Лучше поздно чем никогда.

На рантайме вообще ничего тестить нельзя, учитывая его специфику, все делается на локальном сервере, и просто через Гитхаб обновляются бранчи. Но PHP тем и хорош, что иногда хочется расслабиться и накидать что-то для себя на коленке, не заботясь о типах, как раз таки именно Java в этом плане начинает напрягать, когда ты только и занимаешься тем, что приводишь все к типам. Но в крупных и серьезных проектах это необходимость, да, и очень полезная необходимость.

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


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

Пример того, когда автор может честно говорить, что думает. Да, грубовато. Но большинство статей про js намного токсичнее, хотя автор будет обращаться на «вы».
Эта статья скорее развлекательная. Честность и эпатаж в стиле камеди клаб — немножко разные вещи. Честным было бы показать особенности подхода на примере своих работ с примерами кода и анализом выбранных решений.
** Прыгая в комменты с клавиатурой **
-А холивар то где???
P.S. После фразы «адское говнище» не читал.
Наконец то кто то осмелился это сказать! Я уж думал со мной что то не так
Не зря во многие ЯП добавляют статическую типизацию(PHP, Python, Javascript)

В PHP добавляют строгую. А про джс можно подробнее?

Ну так TypeScript же.
В PHP нет ни строгой, ни статической типизации. Нет и не будет.
Тот, кому первому пришла в голову идея назвать рантаймовый контроль типов «типизацией» — будет вечно гореть в аду за обман джуниоров.

Типизация — не контроль типов?

UFO landed and left these words here

Не согласен. Типы — информация о том, как интерпретировать то или иное значение. А где она хранится и как и когда проверяется — детали реализации.

UFO landed and left these words here

Лучше поздно, чем никогда, нет?

UFO landed and left these words here

Как по мне, то если контроль типов есть, то это типизация.

UFO landed and left these words here

Ваша программа станет типизированной.

Если эта проверка встроена в язык и её нельзя обойти — то конечно язык типизированный. По крайней мере, такое понимание типов согласуется и с википедией, и с официальными документациями языков программирования, и с литературой по программированию. То, что это не идеально совпадает с теорией типов в математике — ну так в разных областях одно и то же слово может использоваться в разных значениях.
UFO landed and left these words here
Не знаю, из какой именно книги ваша цитата выше — но даже в ней написано, что такое использование термина является стандартным. То есть, именно так понимается «динамическая типизация» применительно к языкам программирования. Так зачем спорить о терминах?
UFO landed and left these words here

Большинство источников используют "динамическая типизация" без подобных огооврок.

Понятие вроде «динамически типизированный» есть по логике вещей неверное наименование и должно быть вероятно заменено на «динамически проверяемый» (или «проверяемый во время выполнения» — прим. пер.), но использование этого понятия уже устоялось.
Очень странный подход к определению типов данных. Вот есть языки, которые позиционируются, как языки со стогой статической типизацией. При этом практика диктует наличие в таких языках механизмов позднего связывания (по факту элементов строгой динамической типизации). Получается все данные и структуры, для которых использованно позднее связывание не имеют типов вообще? Это как-то выбивает основу из-под строгой типизации, не находите?
Читать про алгебраический тип данных. Много думать.

Строго говоря можно даже говорить о том, что все языки с динамической типизацией — суть языки со статической типизацией, в которых есть ровно один тип (и других создать невозможно). Ну или (как в JavaScript) — их несколько, но их фиксированное число и они все описаны в документации.

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

Но нет, позднее связывание не делает язык нетипизированным. Даже если в каким-то месте про тип и нельзя ничего сказать (как в Java, когда вы получаете Object), но в других-то можно!
UFO landed and left these words here
UFO landed and left these words here
К сожалению dlsym даёт просто указатель на функцию, которую можно вызывать. А уж как её вызывать — не его дело.
UFO landed and left these words here
Были архитектуры, где именно так и сделано — железо само следит за типами аргументов. Можно ли считать такой ассемблер типизированным?
UFO landed and left these words here
Вы определили «типы» как нечто, проверяемое при компиляции. Но это ведь не единственное определение.
UFO landed and left these words here
Применительно к программированию я, например, встречал определение типа как набора значений и операций над ними. Когда именно проверять, что объекты удовлетворяют такому типу, там не уточнялось.

назвать типизацией наличие проверок на корректный доступ к элементу массива


Тем не менее в Паскале длина массива именно что входила в определение типа.

(ещё до программирования)


Если вы имеете в виду математические типы в стиле введенных Расселом, то он ведь тоже не уточнял, в какое время их надо проверять.

Поэтому ваше утверждение

которое на программирование отображается как статические проверки


достаточно спорно.

Если уж на то пошло, то и статическая, и динамическая проверка типов вообще не относятся к типам, как таковым — типы просто существуют, а является скорее помощью человеку, который не может не делать ошибок и не путать данные разные типов в процессе программирования или выведения логических формул.
UFO landed and left these words here
> Дико неконструктивное определение.

Не только конструктивное, но даже конструктивистское. Т.е. вполне пригодное для практического построения системы типов и ее использования.

Типы в ЯП до формализации примерно так и строились.
UFO landed and left these words here
На практике просто выбирают удобные и легко реализуемые. Для целых чисел, например, знакомые арифметические действия с поправкой на двоичность и ограниченность.
UFO landed and left these words here
Вы путаете инженерию с математикой. Инженерия — это как раз о том, что удобно и реализуемо, а не то, что аксиоматично и строго формализуемо. Математика всего лишь шьет пиджаки для осьминогов.
Должна ли операция «удалить первые N символов» быть в определении строки?


Может быть, но не обязательно. Она не слишком аксиоматическая, что ли.

Если у вас питон с типа строгой динамической типизацией, то, получается, «abcde» и "" — разные типы?


Непонятно, почему вы пришли к такому выводу. Операция эта будет определена как функция отображения строки в строку, т.е. тип объекта не изменится.
UFO landed and left these words here
Значит, это частично определенная функция. Ничего особенного.
UFO landed and left these words here
Иногда так и делают. Например, добавляют к множеству «действительных чисел» NaN и доопределяют деление.
UFO landed and left these words here
Может быть, но не обязательно. Она не слишком аксиоматическая, что ли.

То есть вместо строгого определения имеем: "Вроде как нет, но если надо, почему бы и не да".

Отнюдь. Просто задачу выделения элементарных операций над типом (и определить тем самым тип) можно решить разными способами. Как только мы зафиксировали один из них, дальше все строго.
UFO landed and left these words here
Боюсь, что это не столько теория, сколько практика. И не моя, а универсальная.
UFO landed and left these words here

И это положительно сказывается на популярности?

UFO landed and left these words here
Несомненно. Порядок и логика всегда лучше хаоса и конструкций из палок. Но практика еще и показывает, что математизации поддаются только самые простые части мира, и программирования в частности.

У меня лично ощущение, что "полная" математизация основ языка (теория типов, теория категорий) негативно сказываются на его популярности.

UFO landed and left these words here
некоторые выражения не имеют смысла, не «вычисляя»


Вы все равно вычисляете — ведь это знание не дано свыше, а требует тех же символьных манипуляций. Просто в данном случае есть более короткий способ вычисления — как некоторые интегралы можно посчитать в символьной форме, а не численно. Но в общем случае вычисления все равно придется проводить полностью.
UFO landed and left these words here
В частных случаях. Это просто shortcut — как и вся математика, собственно.
UFO landed and left these words here
выделить массив ровно такой длины


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

А для передачи массива в процедуру приходилось определять формальный аргумент, прибегая к чему-то вроде any: ARRAY OF INTEGER, например, вместо полного типа ARRAY[1..10] OF INTEGER.
Конечно ассемблер типизированная вещь. Типов, правда, не так много: «нечто размера 32 биты», «нечто размера 128 бит»… ну и всё.

Хотя бывают разные ассемблеры. Почитайте документацию на TASM. У них там объекты были.
Не нечто, а вполне определённые форматы, для которых нужны свои инструкции.
Целочисленный add, применённый к float значению, выдаст хурму на выходе.
елочисленный add, применённый к float значению, выдаст хурму на выходе.
Недоумённо смотрит на свой код из релизнутого продукта. А вы точно в этом уверены?

А вот эту статью вы когда-нибудь видели?
На ассемблере add вполне себе выполнится, вот только результат будет весьма прикольный.
Почему прикольный? Всё будет зависеть о того, что вы складываете с чем и зачем.

В моём случае речь шла об округлении мантиссы — это делается как раз использованием целочисленных операций с float.
В этом случае да, но не следует забывать того, что какраз у самого процессора(x86, сопроцессор и прочее не учитываем!) понятия типов вообще нет, и ему глубоко пофигу что с чем складывать. Поэтому 1.1f + 43 вполне себе может вылиться в любой треш.
Тут код специально рассчитан на такое поведение и работает со специально подобранными константами.
Разумеется, какие-то целочисленные операции можно применять к float зная формат и ожидаемый результат.

Я же говорил, что сложив 1+1 вы получите не 2, а 1.7014118346e+38

Точно так же, перепутав знаковое и беззнаковое деление результат может быть неверным.
Я же говорил что сложив 1+1 вы получите не 2
А почему вы, собственно, должны получить 2? Вы и без всяких floatов можете получить чушь, если в одной переменной у вас 1 и в другой 1, только в одной — это метр, а в другой дюйм.

Проверено экспериментально.
Хоспаде. Я написал банальную мысль о том, что при программировании на ассемблере нужно следить за типами инструкций и операндов. Типы отличаются не только размером, но и форматом данных.
Процессор не сделает преобразование типов за вас. К чему вот было это ваше «а можно плавать и со штангой»?

>> А почему вы, собственно, должны получить 2?
Потому что я хочу получить 2, наверное?
Потому что я хочу получить 2, наверное?

Интересный аргумент. А если я хочу получить 3, то должно получаться 3?
Нет, конечно. Лицензия на «хотение», очевидно принадлежит beeruser — и потому только он имеет право чего-то хотеть. Вы опоздали.
А если я хочу получить 3, то должно получаться 3?
1+1=3? Cтранное желание, но как хотите.
UFO landed and left these words here
Проверка выхода за границу массива это не проверка типа? Или защита от выполнения данных?
Ещё, слышал, бывали процессоры, у которых переменные содержали поле с типом.
UFO landed and left these words here
У типа есть очень формально определённое значение

Много определений типа в программировании. Некоторые ещё тянут в программировании определения типов из математики.

UFO landed and left these words here

На любителя. По ощущениям на любителя ФП

"У типа есть очень формально определённое значение".


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

UFO landed and left these words here
Понятие типа в контексте STLC


1. Система типов из «лямбда-исчисления с типами» не единственная система типов, а только одна из.
2. Системы типов в современных мейнстримных языках (как со статической типизацией, так и с динамической) — это очень далеко не STLC и я подозреваю, что их авторы строили их на несколько других основаниях (и не только формальных).
3. Да, можно натянуть сову на глобус (что и делает тапл) и вывести одно из другого, но это вообще не означает, что определение типа из STCL единственно верное или валидное для языков программирования.
4. То что система типов красиво формализуема еще не означает, что она хорошо подходит для промышленной разработки людьми, которым важно получить результат здесь и сейчас, а не формально верифицировать корректность программы.
UFO landed and left these words here
А в каких других теориях типов это не статическая классификация?


Ну есть, например, такая «Gradual Type Theory». Правда я с ней недостаточно знаком, чтобы внятно ее обсуждать.

По остальным пунктам — а о чём мы спорим-то?


Я спорю с утверждением, что «типы — это не рантайм-метки рядом с другими ячейками в памяти, а что-то, что проверяется компилятором статически», и утверждаю, что если «статическая типизация = типы проверяются компилятором статически», то так же правомерно говорить «типы проверяются рантаймом динамически = динамическая типизация».
UFO landed and left these words here

А если система типов есть, но она плохая по этому критерию?

UFO landed and left these words here
automath какой-нибудь возник сильно до любого из ныне существующих языков программирования.


Я думал, что тут разговор о языках программирования, а не доказателях теорем.
Разве пыха компилируется в принципе? Я сто лет не пышник и не в курсе новостей этого вашего пшп 7, хотя про типы и слышал. Просто обывательский подход — если ты не сидишь и не ждешь конца компиляции — оно не компилируется, а выполняется.

Оно компилируется в опкод, который выполняется VM.

В 8 обещают JIT компиляцию в нативный код, а пока только в опкоды

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


Мне кажется, такой подход к терминологии менее ортогонален.

UFO landed and left these words here
Ну так и называются, метки.

А кем они так называются? Есть ли какая-то реализация которая называет их не типом?


Например, в вашей любимой IDE при отладке тип переменной и тип значения переменной называются по разному? Один тип, другой метка?


Ээ, не знаю, это какой-то слишком общий термин для меня.

Ну вы в обычной речи слово тип не употребляете?


С моей точки зрения в книжке терминология интересна но неудобна, все говорят "тип" для общего, никто не использует выражения "рантайм метка" а статика тесно связана с динамикой.

UFO landed and left these words here
С моей точки зрения в книжке терминология интересна но неудобна, все говорят «тип» для общего, никто не использует выражения «рантайм метка»

А зря. На мой взгляд, создаёт неправильные ожидания.

Зря или не зря — это уже больше философский вопрос. Факт в том, что «динамическая типизация» по отношению к языкам программирования используется именно в таком смысле, и причины, по которым так сложилось, здесь не важны.
UFO landed and left these words here
В языке «ться» и «тся» не различаются, они различаются лишь на письме, которое представляет собой условность. Потому, собственно, их и путают на письме.

лишний раз указать на принципиальное различие между типизацией и рантайм-проверками


То, что вы называете типизацией, — всего лишь проверки до рантайма.
UFO landed and left these words here

Это лишь ваше убеждение :)

UFO landed and left these words here

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

UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here

Извините, но вы соответствие Карри-Говарда проигнорировали. Избирательное зрение?

UFO landed and left these words here

Надеюсь теперь всем ясно, что это — тролль?

UFO landed and left these words here
UFO landed and left these words here
Что вы собрались выяснять и в каком споре? Если человек не умеет в логику? В принципе?
UFO landed and left these words here
А… ну это, успехов. Я умею сделать так, чтобы их уволили (что, обычно не так и сложно), а большего мне и не нужно обычно.
UFO landed and left these words here
В политику и я не умею. Но с людьми, не умеющиеми в логику, обычно бесполезно спорить. Нужно просто пользоваться тем, что они не умеют просчитывать результаты своих шагов и «делать их крайними».

Они, впрочем, обычно обладают крайне развитым навыком «переноса ворот» (как вы это уже тут видите), потому важно им всячески помогать, но ни в коем случае не брать на себя никаких обязательсв, если они не скрплены «подписями и печатями».

Даже если вам за выполнение чего-то сказанного мимоходом и нигде не зафиксированного обещают кучу плюшек и всяких благ. Лучше прослыть «ничего не понимающим в бизнесе», чем оказаться крайним, когда очередной такой персонаж будет на вас пытаться повесить свои косяки.
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
А если мне сказали закодить биржевого бота, который будет торговать на какой-нибудь азиатской бирже только в рабочие дни, то, например, если я неправильно скопирую список праздников (или нагуглю список не для той страны), то типы едва ли это помогут отловить, конечно. Но как это отлавливать — вообще непонятно.

Хуже. Список рабочих дней может как в России определяться в предыдущем году по решению Правительства. Или как с "нерабочими" днями. По ходу дела.
Вообще удивительно, что только 0xd34df00d реально вернулся к истокам. Все это программирование — это не код ради кода, а код обработки данных. А все данные типизируются. А код — это просто функции превращения одного в другое.

у него большая проблема: многое из того что он говорит базируется не на научном подходе, а на религиозных предпочтениях/взглядах
Ну хоть с тем, что ЯП со статической типизацией убирают множество проблем с ошибками типов вы согласны?
UFO landed and left these words here
однако надо помнить (и это исследовал ещё Ларри Уолл), что большинство проблем с ошибками типов связаны с тем, что в языках некорректно сдизайнены операторы сравнения и математические операции.
А ещё нужно помнить, что когда эта «глыба», эта «гора», этот «гений» решил создаить что-то на основе своих идей… то получился высер такого микроскопического размера, что о нём даже как о мыши-то говорить смешно.

и чем крута динамическая типизация: что программист больше думает об алгоритме, нежели занимается обрядами вокруг его реализации
Серьёзно? И потому как только вам требуются реально серьёзные алгоритмы (распределённые базы данных или хотя бы SQL-базы, компиляторы, операционные системы и всё такое прочее) — так прям все на динимических языках начинают программировать? Вы это сейчас серьёзно?

Знаете — весь этот ваш пафос был бы слегка более уместен если бы подверждался опытом. И вы могли назвать хотя бы одну систему, где динамически типизированный язык — это не «пенка» на базисе из модулей на статически типизированных языках, а что-то, что сущесвует само по себе. Хотя бы.

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

и это путь решения тех же проблем но на дороге динамической типизации
Это махание руками. Давайте ближе к практике:
1. Реализация динамически типизиванного языка на динамически типизованном языке же: ___
2. Операционная система на этом самом динамически типизованном языке: ___
3. База данных на таком же языке: ___
4. Процент рынка, который вот всё это заняло в ___ году: ___

Вот как заполните пропуски — так сможете лить в уши сказки про преимущество динамической типизации в деле реализации алгоритмов. А до тех пор — это всё рассказы условного «таджика» умеющего неплохо строть туалеты и двухтажные домишки дендрофекальным метордом о том, что у оного метода есть масса преимуществ перед сталью и бетоном, а что какие-то идиоты из говна и палок даже не пытаются строить мосты и небоскрёбы — так это потому что у архитекторов и инжинеров-строителей умишко слабенький и нет того опыта строительства туалетов, что «таджика»…
UFO landed and left these words here
назовите три полезных программы на Расте/Хацкеле стоящие на большинстве компьютеров
Назовите хоть одну такую на Raku для начала. Или вам можно выбирать языки, а мне нельзя? Вы же сами тут поёте песни про крутизну Ларри — ну вот покажите… на практике.

динамически типизированные языки — это скриптовые языки, прежде всего.
Внезапно как, а. А почему так, не расскажите? Почему языки, в которых «программист больше думает об алгоритме, нежели занимается обрядами вокруг его реализации» не применяются там, где алгоритмы сложны и о них действительно приходится думать — но всё больше там, где алгоритмы тривиальны и думать о них не нужно?

затем попробуйте удалить Perl и Bash. и посмотрите на результат
И много вы алгоримов на Bash написали? Я как-то писал топологическую сортировку банальную — то ещё равлечение было. В Android, кстатати, нет ни Perl, ни Bash. И ничего — работает как-то.

А вот попробуйте оттуда удалить модули, написанные на C…
UFO landed and left these words here
Вы хотите сказать что языки со строгой/статической типизацией все находятся в стадии «бета» (== «ещё не доделан»)?
Я хочу сказать, что с идиотами, записывающими в языки с динамической типизацией C и Java разговаривать бессмысленно. Хотя вас я идиотом не считал, но… теперь вижу._
UFO landed and left these words here
UFO landed and left these words here
Эти все доводы работают в каком-то выдуманном виде. Где «венец творения» это не "рюмка коньяка с ломтиком лимона документооборот", а какие-то дурацкие вещи типа смартфонов и космических кораблей.

Статически типизированный язык, между прочим

Типизация слабая, да. Но даже при всём при этом в OpenSSL уязвимостей куда меньше, чем в каком-нибудь Drupal. А уж если выкинуть всякие «module that can only be compiled by the HP-UX assembler, so that only HP-UX PA-RISC targets are affected»…

Да, предствьте себе — даже такая слабая типизация, как в C, и даже при такой ужасной культуре кода, как в openSSL (поговорите с теми, кто внутрь смотрел) всё равно снижает количество уязвимостей. В каком-нибудь NGINX — их меньше на порядок. В Chrome — да, побольше будет… но вы когда-нибудь сраванивали по объёму Drupal и Chrome? Сравните как-нибудь на досуге.
поэтому языки вроде C, C++, Java (и прочие языки традиционно ориентированные) — это языки, которые я противопоставляю высказываниям сектантов.
У… как всё запущено. Что такое вообще «традиционно ориентированный язык»?

именно строгую типизацию сектанты вроде 0xd34df00d противопоставляют тестам.
Серьёзно? У вас всё с логикой настолько плохо?

Извините, но я нигде и никогда не слышал, чтобы 0xd34df00d говорил о том, что типами нужно заменять тесты. Он всегда говорит о том, что можно — и да в Idris это попроще, а в C++… ну на спор, наверное, тоже можно, но в реальной программе — не получится.

Вопрос того, что нужно выражать ограничениями на типах, а что лучше оставить в виде тестов — он совершенно отдельный от вопросов принципиальной реализуемости того или иного подхода.
UFO landed and left these words here
А какое вообще вопрос «кто кого любит и как» оказался связан с языками програмирования?

И, кстати, как вы вообще выясняете ориентацию программистов на Haskell или C++?
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
Давайте теперь Вы назовите пару монополистов, имеющих аудиторию в миллиард людей, чтобы их основной язык был со строгой/статической типизацией
Вы издеваетесь или как? Ну пусть будет Google и Microsoft, если уж так хотите. Только не рассказывайте сказок про то, что Microsoft меньшая монополия, чем какой-нибудь Facebook: в китае без Facebook отлично живут, а без Window — таки не обходятся. Ну или Apple возьмите — да, это не монополия… но денег она зарабатывают больше, чем Facebook и Mail.Ru вместе взятые.

то FaceBook — это PHP.
Нет. PHP такую махину не потянет. Facebook — это Hack. И да — он статически типизирован.
UFO landed and left these words here
В Касперском что-то пилили на хаскеле. По крайней мере, когда тамошние рекрутёры общались со мной N лет назад.

поговаривают, что там лоно адептов крестов и Раста

Да-да. Нанимают сишников-системщиков и заставляют их монадки моноидировать.

> Booking.com — это Perl (сейчас мигрируют на Python, просто Python'щики готовы работать за еду, поэтому)

На Java. Какой смысл уходить на Python для такой нагруженной задачи?
(распределённые базы данных или хотя бы SQL-базы, компиляторы, операционные системы и всё такое прочее) — так прям все на динимических языках начинают программировать?


Однако же поверх всех этих замечательных программ тут же возникают динамические языки — шеллы или тот же SQL. SQL сильно типизирован?

Совпадение? Не думаю.

Так никто вроде бы и не спорит, что в качестве glue code для одноразовых задач динамические языки вполне себе работают.

Некоторые SQL запросы переживают несколько смен языка арр-сервера

Совпадение? Не думаю.
Нет, конечно. Как только вы решаете, что вам не нужен качественный код, но нужны дешёвые программисты — так динамические языки становятся, вдруг, резко осмысленными.

Программисты на PHP получают меньше, чем программисты на C++, а администраторы («программисты на bash») — ещё меньше.

В некоторых случаях возможна и обратная ситуация (финансовый аналитик, пишущий только программы на каком-то простеньком язычке, но никак не на C++ — может получать и больше программиста на C++), но в этом случае они получают столько не за то, что умеют лихо писать программы на Python, а за что-то совсем другое.

Где-то тут уже приводил: в Киеве разница между PHP и Javaсеньорами порядка 5% всего. Это во столько бизнес оценивает надежность статической типизации (забудем про то, что часто Java и быстрее)?

Опять бред несете. Вы живое доказательство того что динамика а особенно PHP разжижает мозг.

Поищите — это публичная статистика, я ссылки приводил в топике

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

к чему тогда пассажи про "нужны дешёвые программисты — так динамические языки становятся, вдруг, резко осмысленными. Программисты на PHP получают меньше, чем программисты на C++"


У меня есть цифры, что эти "дешевые" лишь на 5% дешевле, а разница в качестве, вроде как, качественная, если верить адептам статики.

У меня есть цифры, что эти «дешевые» лишь на 5% дешевле
Нет у вас таких цифр, извините. У вас есть информация про кое-что другое.

Разница между дешёвыми и дорогими программистами лишь слегка кореллирует с зарплатой.

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

Подумайте над этим.

а разница в качестве, вроде как, качественная, если верить адептам статики.
Разница качественная — но не между динамикой и статикой.

А между продукцией «дешёвых» и «дорогих» программистов.

Я это уже показывал на примере CVE.

И да, разница между зарплатами — гораздо меньше, тут вы, что забавно, тоже правы.
UFO landed and left these words here
Не в возражение основному, но:

> В языке «ться» и «тся» не различаются, они различаются лишь на письме, которое представляет собой условность. Потому, собственно, их и путают на письме.

В языке у них разная роль, что можно увидеть, например, по тому, что для некоторых глаголов вместо "-ться" получается "-тись": нестись, пастись…
(это как раз о типизации;))

В фонетике, да, они сливаются — но уже после этого.
Они не различаются на слух, следовательно, со временем это будет один и тот же грамматический элемент, исторически произошедший из двух разных.
1. Это не грамматический элемент.
2. С чего это бы им становиться одним элементом? У большинства глаголов таки тут нет совпадения, случаи типа «храниться»:«хранится» — малая часть.
И если мы обсуждаем преимущества разных видов типизации, то, ИМХО, лишний раз указать на принципиальное различие между типизацией и рантайм-проверками, которое по-хорошему должно быть определено даже в терминологии, вполне себе стоит.

Так никто не против указывать на различия статической и динамической типизации. Более того, никто вроде не отрицает, что с некоторой точки зрения правильнее эти две альтернативы называть по-другому. Но для того, чтобы как можно больше людей, связанных с программированием, вас сразу без дополнительных пояснений правильно понимало, нужно использовать именно «статическая типизация» и «динамической типизация» — это устоявиеся названия классов языков. Можно для себя их называть как угодно, но все (?) официальные документы по динамически типизированным языкам программирования используют слова «тип» и «динамическая типизация»/«динамическая проверка типов» — например python, js. То же верно и для обсуждений этих языков на практике. Поэтому смена терминологии привнесёт только путаницу на этом этапе.
UFO landed and left these words here
В моей IDE для хаскеля вообще нет рантайм-меток (да, я за всю практику пользовался Typeable в своём коде ровно один раз). Да и дебаггером я там не пользуюсь.

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


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


В моей IDE для плюсов их тоже не особо много для рантайм-поведения.

А что у вас за IDE для плюсов? У вас там нет cимволов и RTTI? в окне watch нет колонки type для переменных? Или там написано что-то типа "рантайм метка относящаяся к набору операций которое можно совершать со значением"?


А зря. На мой взгляд, создаёт неправильные ожидания.

Какие и у кого?


Не обязательно. В том же хаскеле в общем случае после того, как вы проверили типы, вы можете их стереть и не иметь вообще ни намёка на них в рантайме.

Я хаскель знаю очень поверхностно. Мне трудно с этим поспорить.


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

UFO landed and left these words here
type-hints
прекрасный термин, и никого не обманывает
В архитектуре «Эльбрус» такие аппаратные метки назывались тегами.
Думаю в PHP, как и в питоне можно проверять линтерами не в рантайме.

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

Можно писать на Idris и эммитить код на PHP

Разве только чтоб обмануть работодателя (которого примерно бесконечно проще найти для PHP, чем для Idris).
Тот, кому первому пришла в голову идея назвать рантаймовый контроль типов «типизацией» — будет вечно гореть в аду за обман джуниоров.


А как по вашему это нужно называть?
Контролем типов, как и указано в мануале.

Типизация в PHP как была динамической и слабой, так и осталась. И нет никаких предпосылок к изменению этого положения.
Контролем типов
Вроде это по определению делается во всех системах с проверками типов. Ну там Rust, Haskell. На другом этапе, конечно.
Я не понимаю отчаянного сопротивления применению определенного уважаемого термина к РНР и попыток его замены на какой-нибудь другой, не такой уважаемый.
Типизация в PHP как была динамической и слабой, так и осталась
Кстати, вы будете гореть в аду
Тот, кому первому пришла в голову идея назвать рантаймовый контроль типов «типизацией» — будет вечно гореть в аду за обман джуниоров.
UFO landed and left these words here
Если это не type (тип). Каким словом обобщить свойство поведения «строка», «число», «интерфейс» в ЯП? Чтобы можно было корректно спросить «Какой blabla у этой переменной?» и другой разработчик понял вопрос.
Если это не typing (типизация), то как сообщить другому человеку «Я придумал ЯП с динамическим blabla» и остаться понятым?
UFO landed and left these words here
«Можно ли на этой переменной дёрнуть эту функцию?»
Но если мы хотим донести, что эта переменная ведёт себя как string ибо помечена таковой средой выполнения, то нам придётся долго перечислять список функций (и всё равно можем не попасть, потому что у другого типа может быть такой же, но он к примеру несовместим со string). Нам же нужны обобщения.
вы придумали язык без статической типизации.
Это можно, да. Термин взаимоисключающий.

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

Я не знаю, как это было с исторической перспективы, но сейчас частичное пересечение узкоспециального термина с широким общим играет отрицательную роль для первого.
UFO landed and left these words here
Понятие типа — хоть в номинативном, хоть в структурном смысле — вполне может пригодиться в динамическом языке. Например, для перегрузки функций: перемножить две плотные матрицы — один метод, плотную и разреженную — другой, разреженную и диагональную — третий; сложить элементы с i-го по j-й — один метод для типа, который имеет доступ по индексу, другой для типа который позволяет только итерироваться. И т.п.
Но да, это не про питон.
Можно просто сказать, что вы придумали язык без статической типизации.

Это может быть и безтиповый язык типа популярных ассемблеров.

А еще смешнее, что нет контроля за этим контролем.
А джунам тем стоит взять JSP если уж на то пошло)
В PHP 7.4 для полей классов завезли именно статическую.

В "компайл-тайме" в клиентской коде мы не можем знать что загрузит автолоалер или какая имплементации интерфейса придёт. Вообще проверки не будет?

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

Ну какой-то компайл тайм есть: преобразование в опкоды. В рамках одного класса можно было бы в нём что-то по минимум проверить, но вот весь проект проверить нереально, по крайней мере с доминирующей моделью автолоадинг классов.

Пока нет 8 и нет JIT, ни о какой компиляции мы не можем говорить =)

Java на JIT, насчёт C# не могу сказать ничего. А 8 с JIT ещё только в альфе, первый релиз-кандидат вроде как только осенью будет, а сам релиз в декабре.
В пыхе компиляция пока что относится только к сборке самого бинарника руками) А наш с вами код интерпретируется, это сильно другой процесс.

В альфе, но есть :)


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

Вопрос ведь не в терминах, ну и не спорю, если грубо — то можно свести к фразе компилится.
Для меня лично компиляция — строго вне рантайма. Если код попадает в кучу когда пришли данные на обработку — интерпретация. Отсутствие файлика — огромная разница в процессе.
Ну и ещё у меня стойкое чувство, что вы меня стебете)
/ пошто пыхоиндуса обижаете? :( /

Ну вот JIT тогда не компиляция? Файлика нет же. :)


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

В динамических языках (и Форте) компилятор всегда присутствует в системе и может быть вызван в любой момент, в том числе и во время исполнения.

У вас слишком узкое представление.

Форт я ещё в самом начале 90-х затруднялся к чему отнести, настолько у него режимы интерпретации и компиляции перпелетены.


P.S. Точнее — сшиты :)

Не во время компоновки/загрузки?

PS На всякий случай подчеркну, это вопрос, а не утверждение.
Если кратко:
Проверка типов происходит точно на рантайме, когда данные переданы в поток (то есть код уже в куче)

Если развернуто:
1. В зависимости от версии пыха и правил типизации (strict_types) проекта на этапе разработки (локально) доступны:
1.1. Анализ самого IDE в режиме реального времени
Картинка
image

1.2 Встраиваемые пакеты для статического анализа codestyle
Картинка
image

1.3 Встраиваемые пакеты для стат анализа codequality
Картинка
image


2. Дальше в крупных проектах CI/CD, со стендами для предварительных тестов регрессии, интеграции, фича тестов и вероятнее всего 1.2 и 1.3 повторные.
Картинка
image


3. Дальше, в зависимости от критичности проекта, может быть ряд canary продакшн серверов, на которых крутятся «свои» юзеры, которые выступают в роли кроликов-тестировщиков.

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

А так, по топику могу сказать одно.
Типизация нисколько не спасает от багов на проде.
Чаще всего прод на пыхе страдает от кривой логики реализации бизнес-процесса или не до конца протестированных юзкейсов.
Орут о величии строгой типизации над динамически типизированными языками в основном фронтендеры, которые пересели с js на ts и решили, что они не верстальщики, а программисты =)

Про нгинкс погорячился, в голове джакарта и джетти)

Нет. Вы очень глубоко ошибаетесь. «Статическая» — это на этапе компиляции. В PHP же любой контроль типов, даже контроль типов свойств классов и объектов — рантаймовый.

Для полей классов таки да.


private string $a = 10;

отлупит на этапе компиляции

тут скорее идет разговор не о компиляции как о таковой, а о самом моменте.
Компиляция всех файлов происходит в одно время или в разное. В PHP в момент обращения к файлу.
В Python не добавляют статическую типизацию, вы заблуждаетесь.
Хинты с mypy всё больше используется, по-моему. Как бы становясь best practice и правилом хорошего вкуса. Субъективно, конечно, потому что Python сообщество — тот еще (un)pythonic пузырь.

Не так плоха динамическая типизация, как ее сочетание со слабой, js, php привет вам.
Вот это уже взрывная смесь по производству багов.

а какой язык не слабый?
«Сила» типизации Python преувеличена:
print((not None) + 7)
Сначала None автоматически преобразуется в bool, потом bool автоматически преобразуется в int.
Типичная слабая типизация.

Да, у Python отсутствует автоматическое преобразование число<->строка и возможности преобразования None ограничены. Но в остальном PHP может обеспечить более строгий контроль типов. Аннотации типов аргументов подпрограмм в Python не обеспечивают контроль типов — в отличие от PHP, в котором реализуется реальный контроль и типов аргументов (с возможностью отключения преобразования число<->строка), и типа возвращаемого значения.
Сначала None автоматически преобразуется в bool, потом bool автоматически преобразуется в int.
Типичная слабая типизация.

Оператор not возвращает True или False. Тип bool унаследован от int, поэтому в арифматическом смысле True всегда равен 1, а False всегда равен 0. У вас не получится сделать None + 1.

gaal@catalina monitoring % python3
Python 3.7.6 (default, Dec 30 2019, 19:38:26) 
[Clang 11.0.0 (clang-1100.0.33.16)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> print((not None) + 7)
8
>>> print(None + 7)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
Главная фишка вот:
Python 3.7.5rc1 (default, Oct  8 2019, 16:47:45) 
[GCC 9.2.1 20190909] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> not []
True
>>> not "Oops"
False

Операция «not» применима к чему угодно. То есть там «None» не преобразуется в Bool. А что True/False — разновидности целых… это странно, но это Python унаследовал от C. Он ещё парочку странностей от него унаследовал…
То есть там «None» не преобразуется в Bool.

Спасибо за разъяснение, но я этого и не утверждал. Из моего сниппета четко видно, что None — это инстанс NoneType. А не Bool. А особенности работы not… ну, спасибо.

UFO landed and left these words here
В моих книжках написано, что not (not x) = x для всех допустимых x, значит, None эквивалентно not (not None).

Ещё раз. У вас не получится сложить None и 1. Оператор not это логический оператор отрицания, который возвращает строго True/False. Двойное инвертирование True вернёт True и наоборот, эта цепочка из not not not [...] может быть бесконечной. Не понимаю, что мы обсуждаем?

Да, это всего лишь ещё один способ указать на unsoundness языка.

Ну, назовите это консистентностью языка. В целом проблемы нет, согласен.

Все языки делятся на два непересекающихся класса, к сожалению: неконсистентные и мёртые. В Haskell, скажем, Monad это не Applicative именно по той причине что лучше быть неконсистентным, чем мёртвым.
UFO landed and left these words here
Да, есть unsound-элементы, но они связаны с изначальными ошибками (или компромиссами) в дизайне системы типов, и о них думают, как бы их устранить.
Лучше бы они подумали как «людям снаружи» дать доступ ко всему этому.

Либо объявите GHC «единственной правильной версией» (и тогда версии языка будут соответствовать резлизам GHC), либо сделайте уж, как в C++, регулярные релизы. А то официальной версии 10 лет, а что из бесконечного количества расширений и дополнений, доступных после этого, считать «официальной частью языка» — «снаружи» понять невозможно.

Далеко не у всех есть возможность следить за всей «движухой», если они хотят попробовать Haskell на примере задачи генерации какого-нибудь отчёта.

А с устранением косяков вопрос сложный, на самом деле: в Python считается идеоматичным не писать в if всякие "== 0" или "== []" (хотя лично я это считают некрасивым как раз) из чего, как бы, очевидно следует, что и not должен работать со всеми типами — иначе будет нелогично.
UFO landed and left these words here
Де-факто это уже давно так.
А как до этого догадаться? Захожу я на www.haskell.org (а куда надо было зайти?), открываю раздел с документацией — первым делом, первой ссылкой, меня отправляют на Learn You a Haskell for Great Good!, где есть прям целаю душешипательная история про то что Монада — это не Applicative. Ладно, это тьюториал, они часто не поспевают за развитием языка. Ищем описание языка… единственное, что там есть — это Haskell 2010. Если погуглить — можно на wiki найти информацию про Haskell'… ссылка ведёт на сайт, который не отвечает, а страничка на archive.org радостно сообщает, что Haskell Prime 2020 committee has formed — «свежая» новость от 2016го года.

Ну и куда мне идти, чтобы что-то узнать, а главное, как до этого догадаться?

Сравните с C++. Wikipedia отправляет на isocpp.org. Там есть анносы GCC 10.1 (релиз от 11 мая 2020го), есть ссылка на Core Guidelines, можно добраться до драфта (хотя было бы полезнее, если бы ссылка была бы поближе к корню isocpp.org, а так туда приходится идти через cppreference).

А где у Haskell-community что-то подобное?

P.S. У C++-комьюнити есть, правда, своя, особая фишка: бесконечные draftы. Попытка найти хоть чего-нибудь отрелизнутое — обречена на неудачу. Нужно некоторое время «повариться», чтобы понять, что это — следствие бюрократии ISO, которое привело к тому, что все и всегда используют draftы. Релиз, типа-вроде-как окончательная версия, никого не интересует настолько, что если draft будет говорить одно, а релиз — другое, то реализуют именно draft: их используют разработчики компиляторов, программисты и вообще все, кто мало-мальски интересуется C++. А релизы? Ну их ISO за деньги продаёт, можно купить и положить на полочку. Всё. Больше в них никакого смысла нету. Да, этот «секрет Полишинеля» сходу, на web-сайте не найти…
UFO landed and left these words here
ruby, 1 + "hello" => TypeError (String can't be coerced into Integer)
А я говорю, что описание типов — и есть описание процесса
Э? Вообще-то описание _процессов_ — это функции/процедуры.
и у функции есть интерфейс, а именно аргументы и возвращаемое значение, чьи типы хорошо бы знать
Обе системы типов (статическая и динамическая) имеют преимущества.
Код на динамических языках не только пишется легче, но и легче читается. Поэтому многие ошибки видны невооруженным глазом.
Но с определенного размера кодовой базы все связи уследить уже просто не возможно, и вот тут на помощь приходит статическая типизация.
В общем, для каждой задачи — свой инструмент.

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

Вывод типов — это хорошо, но работает он не всегда. Так или иначе компилятор все равно хочет «подсказок». По крайней мере в тех языках, с которыми работал я.
UFO landed and left these words here
TypeScript предлагает не самый удобный синтаксис, где декларация типов и имплементация смешиваются в одном предложении. Подход за который топит автор — сначала пишем типы, потом все остальное — не работает чисто синтаксически.
function foo (string) : string { } // error 

В хаскеле сделано удобнее. Но это имхо и вкусовщина, не буду спорить если у кого-то другое мнение.
function f(a: number): string;


попробуйте так
Потому что имена аргументов, это так же часть сигнатуры и она важна для понимания, что функция делает.
В Вашем варианте foo я понятия не имею, что за строку она от меня хочет, но стоит написать вот так:
function foo(userName: string): string;
и все стало гораздо понятнее, хотя foo по прежнему не очень удачное имя…

И да, по-нормальному было бы вообще так:
function foo(userName: UserName): string;
но убогая структурная система типов тайпскрипта не дает это выразить нормально

Вобще-то даёт. Гуглите брендированные типы.

Знаю я про них, но они все равно не работают нормально, ибо структурная типизация…

Всё прекрасно работает.


import {
  $mol_data_nominal as Unit,
  $mol_data_integer as Int,
} from "mol_data_all";

const Weight = Unit({ Weight : Int })
const Length = Unit({ Length : Int })

let len = Length(10)
len = Length(20) // Validate

len = 20 // Compile time error
len = Weight(20) // Compile time error
len = Length(20.1) // Run time error

let mass: typeof Weight.Value
mass = len // Compile time error
В итоге всё свелось к Delphi/Pascal.
Вы плохо знаете историю. Это всё ALGOL.

А Delpha/Pascal — это уже «закат эпохи». Когда теоретики заизолировались у себя в башне, а практики начали делать «удивительные открытия», известные теоретикам в середине прошлого века.
UFO landed and left these words here
Буквально вчера делал у себя на проекте похожую штуку. Работает :)

type UserName = string & { readonly tag: unique symbol };
type Password = string & { readonly tag: unique symbol };

const nameOf = (name: string) => name as UserName;

function stringOf(name: UserName): string {
  return name;
}

stringOf(nameOf("bingo347")) // OK
stringOf("bohdan-shulha") // Argument of type '"bohdan-shulha"' is not assignable to parameter of type 'UserName'.
stringOf("hellowrld" as Password) // Argument of type 'Password' is not assignable to parameter of type 'UserName'.

const nameOf = (name: string) => name as UserName;
Вот я и говорю, что это не работает, любую строку можно просто привести к типу UserName без доказательства последнего
А как доказать, что строка, которая пришла с сервера, это действительно UserName, а не что-то иное? Предполагается, что приведение типов будет использоваться в фабриках, а по приложению будут ползти уже Value Object. Этот способ, как минимум, гарантирует, что нельзя будет случайно передать рандомную строку вместо UserName.

Я могу ошибаться, но вы именно о таком поведении писали в комментарии выше.
А как доказать, что строка, которая пришла с сервера, это действительно UserName, а не что-то иное?
Проверить, что она соответствует всем ограничениям на тип UserName, если проверка успешна — я получу тип UserName, иначе получу ошибку. Другого способа получить тип UserName в программе нет, поэтому ему можно доверять. А вот типу, в который можно просто кастануть любую строку я доверять не могу, он для меня бесполезен.

Вот чего мне в TypeScript не хватает так таких проверок в рантайме. Писать их руками может быть очень утомительно, хорошо если хоть базовую проверку сделаешь перед тем как в гварде true вернешь.

Конечно динамическая типизация может иметь свои преимущества у определённых ситуациях. Например когда пишешь какие-нибудь матмодели или что-то в этом роде.

Но я согласен с автором что «в продакшене» всё-таки лучше использовать языки со статической типизацией.
UFO landed and left these words here
К сожалению, не видел еще удобных средств в менйстримных ЯП, позволявших легко избежать складывания условных int и int (где первый — метры, второй — секунды). Надо оборачивать, а всем влом. И библиотеки всякие все равно будут принимать и отдавать int-ы.
UFO landed and left these words here

В плюсах же как раз есть пользовательские суффиксы.

UFO landed and left these words here
Что Вам там больно? «4s + 500ms» сработает и даст четыре с половиной секунды, а «4s + 500m» не скомпилится, поскольку секунды с метрами не складываются.
Описать всевозможные метр/сек кг/сек итд.
Так вот же и статья об этом: либо мы описываем все типы и получаем типобезопасность, либо мы складываем футы с метрами и наш марсоход пролетает мимо Марса (true story).

Ну и, кроме того, всё уже сделано до нас: Boost.Unit
Если нет boost то надо вручную всё это делать.

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

UFO landed and left these words here

Там ограничения есть, например, для целого это всегда long long int, а зачем мне это, если я метры хочу только int32.
Другое дело, что можно все в классы обернуть… тогда точно, одно с другим не сложишь. Но это конечно дополнительно писать придется кода...

В C++ как раз сложишь, если оператор + переопределишь :)

И как так просто метры с миллиметрами сложить? Переопределить то можно… придется делать столько этих операторов, сколько типов собираетесь складывать.

Для такого есть std::ratio

Хорошо, Фаренгейты с Цельсиями.

Вообще не проблема: Фаренгейты с Цельсиями складывать в принципе нельзя (даже в физике), но и то и другое можно перевести в Кельвины или Ранкины. И там уже складывать.
UFO landed and left these words here
UFO landed and left these words here
Именно что аналогично — то есть неизвестно что получится в результате. Кельвины и Ранкины скаладывать можно, а Цельсии, Фаренгейты и Реомюры — только если знать что они означают: собственно температуру или разницу температур. При этом температуру с температурой складывать нельзя.
температуру с температурой складывать нельзя
Вот у меня литр воды 20 градусов и 5 литров 50 градусов, как мне посчитать температуру смеси (пренебрегая теплопередачей посуде и воздуху)? Всегда думал что для этого нужно средневзвешенное значение находить (в кельвинах), а для этого множить на скаляры и складывать.
Научный коммунизм вами усвоен на отлично, цитаты вы дёргать научились. А если хотите увидеть ответ — то прочитайте все пять строчек. Это не так много.
Вам нужно описывать операцию среднего взвешенного. Чтобы библиотека (условно) через unsafe получала скаляры, делала математику и возвращала усредненную температуру. Складывать температуру нельзя, можно усреднять. Т.е. можно складывать только при условии последующего деления, например.
Самое простое — это сделать такие операции:
[цельсий] — [цельсий] = [кельвин]
[кельвин] + [цельсий] = [цельсий]
ну и для кельвина всякие сложения друг с другом и умножения на число.
И (хоть я настолько и не люблю доведение до такого) поэтому в VHDL надо вообще всё полностью описывать, включая преобразования.
Фаренгейты и Цельсии изоморфны, можно перевести одно в другое и сложить.
В том-то и дело, что они нифига не изомрфны. Сколько будет 1°C + 1°F? А фиг его знает: может быть 159°C, может быть -3049°C. И без дополнительной информации вы это не узнаете.

Да, тут есть неоднозначность, каким должен быть тип результата, но это вполне может зависеть от вызывающего кода, и какой тип он там ожидает.
Если бы речь шла только о типе результата — беды бы не было. К сожалению меняется ещё и значение этого самого результата.
UFO landed and left these words here
1°F может быть как температурой (то есть, соответственно, -314/9°C), так и разницей температур (тогда это всего навсего 5/9°C). Точно также 1°C может быть как температурой (тогда это 33.8°F), а может быть и разницей температур (тогда это 1.8°F).

Результаты, как несложно заметить, будут сильно разными.

Потому — только перевод в Кельвины (ну или, если очень приспичит, в Ранкины), потом что-то там можно считать…
std::chrono::duration и std::chrono::time_point решение проблемы для времени.
UFO landed and left these words here
Просто группа градусов как дельт действует (ну как в алгебре) на множестве градусов как температур с привязкой к абсолютному нулю или ещё чему-нибудь.
Не совсем так. В отличие от времени для температуры ноль имеет чёткий физический смысл: это средняя квадратичная скорость поступательного движения молекул (вернее пересчитывается в неё через постоянную Больцмана. Потому для неё не нужны все эти сложности.

Но это только в Келвинах или Ранкиных.

Если же вы хотите что-то считать в Цельсиях или Фаренгейтах… то да, можно развести весь этот дуализм… но обычно не нужно. Ибо всё равно запутаетесь.
UFO landed and left these words here

Вы как-будто в школе физику не учили. Первым делом в любой задаче было привести все параметры к СИ. С другой стороны это очень странная система, если вам приходится складывать такого рода значения. Но в целом проблема N+1 операторов существует. В соседней ветке предложили использовать Boost.Units для таких штук, но если я правильно понял доку, то там собственно все для единиц измерения СИ и его альтренатив вроде СГС. Если нужны будут свои собственные еноты на парсек в час, то кучу бойлерплейта писать все равно придется.

Все верно, в этом и есть задача. Она ничем не отличается от задачи метры +милиметры.
Складывать градусы с градусами можно. А вот метры с градусами нельзя.

В С++ можно так. Код не скомпилился.
int meters = 1;
std::chrono::seconds seconds{1};
auto val = meters + seconds;


А библиотеку обернуть:
int flib_mul2(int i){
    return i * 2;
} 

std::chrono::seconds mul2(std::chrono::seconds i){
    return std::chrono::seconds{flib_mul2(i.count())};
} 

int main()
{
    std::chrono::seconds seconds{1};
    auto seconds2 = mul2(seconds);
}

ну для времени это частный случай. я про отсутствие средств в целом, чтобы можно было легко отличать один intы/doubleы от других. Секунды, килограммы, 1/дж^2. И чтобы иметь возможность запихать их в формулу и получить КГ/АМ если надо, но с проверкой, что именно КГ/АМ получаются

Haskell же!


{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype Seconds = Seconds { getSeconds :: Int }
    deriving Num

Rust же!


use derive_more::Add;

#[derive(Add)]
struct Seconds(u32);
в этой ветке комментариев я сразу сказал, что жаль, что в мейнстриме такого нет :)
UFO landed and left these words here

Да это вроде и на Haskell не очень выглядит.

boost::units как раз похожее и делает (API немного мутноват правда), наверное следующим шагом наверное было бы eigen::vector<boost::length, boost::pressure, boost::force>

Например squants:


scala> val energyUsed = 100.kilowatts * (3.hours + 45.minutes)
energyUsed: squants.energy.Energy = 375000.0 Wh

scala> val energyUsed = 100.kilowatts + 3.hours
                                          ^
       error: type mismatch;
        found   : squants.time.Time
        required: squants.energy.Power

В F# есть такая встроенная фича, называется units of measure. Очень удобная, в моём физическом коде пару ошибок помогла поймать.


Во многих других функциональных языках, в которых есть конструкции вида newtype, это также делается достаточно изящно.


(забавный факт: автор обсуждаемой статьи как раз тоже топит за F#)

В F# есть такая встроенная фича

Там степени только целые, а хотелось бы рациональные иметь.

Вроде в какой-то версии это допилили. У меня работает, например, такое:


[<Measure>] type cm
[<Measure>] type xx = cm ^ (1/3)

let a = 10<cm>
let b = 10<xx>

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

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

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

В гауссовой системе единиц (фактический стандарт в теоретической физике), а равно в СГСЭ и СГСМ такие размерности.

В обработке сигналов часто используется сректральныя протность в удиницах вроде V/sqrt(Hz)

Я бы сказал что это уже от моделей зависит. Иногда как раз и интересно что за зверь в итоге всплывёт.
Этот момент — проверка единиц измерения — весьма слабо связан именно со статичностью типизации. В статически типизированных языках проверка на соответствие условно string или int производится при компиляции, в динамически типизированных — во время выполнения. Точно так же (на том же этапе) делается и проверка единиц измерения — то есть, это реализуется и в статических, и в динамических языках соответствующими библиотеками.
UFO landed and left these words here
Так вы сами подтверждаете, что проверка единиц измерения не зависит от «статичности» типов в языке. Просто единицы проверяются тогда же, когда и стандартные типы — в условном питоне во время выполнения, в условном с++ во время компиляции. Но сама проверка единиц измерения полезна и в питоне тоже.
UFO landed and left these words here
благодаря динамической типизации очень понятен numpy(сарказм)
Говнище, братан, это ваши ооп и типы. ну вот зачем лезть в чистый мир JS со всем этим барахлом?

От всех эти еретических ограничений чист: статика, строгость. Что хочешь, то и воротишь. Почти ассемблер. :)

Своей непорочностью от всякого говнища, братан.

Этот «шарпист» порвался.
Несите нового!
Динамическая типизация — адское говнище

Погодите это про отсутствие типов а ля питон или разрешения типов компилятором в F# перед компиляцией?

Кто сказал что в питоне типов нет? Там их объявлять не нужно.


type(1.5)
<type 'float'>
type(5)
<type 'int'>
type('hello')
<type 'str'>


Ох уж эти три треугольные скобки >>>

Кодоблок ``` в помощь

UFO landed and left these words here
Супер! Я по заголовку сразу же угадал автора, стиль однако!
Хм… И почему тогда все (многие?) языки со строгой типизацией все больше скатываются к добавлению «динамических» типов, типа std::any в С++???

Может все-же в некоторых ситуациях оно таки надо, м?

Да и те-же темплейты в C++ это шаг в сторону динамических типов…
А может имеет смысл понимать разницу между «строгий/нестрогий» и «динамический/статический», м?
Я не утверждаю что он чисто динамический, но это определенно шаг в ту сторону.

Он шаг в сторону строгой типизации как раз


template <typename T>
class Summ
{   
    T x; 
 public:
    Summ(T value): x(value) {};
    Summ(): x(0) {};
    Summ operator+(Summ const& rhs) const
    {
        Summ result ;
        result.x +=  rhs.x ;
        return result ;
    }
};

  Summ<int> sum0(0);
  Summ<float> sum1(2.0f);
  sum0 = sum0 + sum1 ; //Такое не проканает

  int sum00(0);
  float sum01(2.0f);
  sum00 = sum00 + sum01 ;  //а такое проканает
sum00 = sum00 + sum01 ;  //а такое проканает

Предупреждение C4244: преобразование «float» в «int», возможна потеря данных
Warnings as errors и такое не проканает.

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

Это обычный ворнинг MSVC.
Для GCC есть -Wconversion
Естественно, что он не включён по умолчанию, так как обычно это ненужно.

Опции компилятора для того и есть, чтобы настроить под конкретные нужды. Компилировать без настроенных флагов, значит полагаться на дефолтные значения. Далеко не факт, что это те настройки, что требуются.
UFO landed and left these words here
А каким образом темплейты — шаг в сторону динамики, непонятно.

Ну как-же. Вы ведь можете подставить в темплейт любой тип, или переменную, т.е. строгого типизирования нет. Понятно что с точки зрения компилятора все будет все равно типизировано строго, но с точки зрения программиста чем не «динамический» тип?
Собственно концепты и ввели чтобы изобразить что-то вроде типизирования для темплейтов.
UFO landed and left these words here
std::any a = std::string(«fas»);
std::any b = a;
std::any c = a + b;
Не съел компилятор, и вряд ли когда-то съест. any всё таки контейнер для хранения неизвестно чего, а не динамический тип.
any почти наверное означает, что где-то сделана ошибка

Либо что оно прилетело оттуда, где у вас нет власти

В играх ECS без std::any довольно сложно представить, особенно когда это дело еще из сети откуда-нибудь качается.

UFO landed and left these words here

Если речь про примеры кода, то сходу едва ли. У нас используется для сериаизации-десериализации данных с нашей админки в основном. На основе json питонячий скрипт генерит шаблоны полей, магия макросв и бустовых лексеров и бустового же any (который собственно предок std::any) рождают на свет класс в который собственно грузятся данные с админки. Есть, конечно, альтернативы вроде того же protobuf с похожим пайплайном, но под наши задачи он не подходил т.к. для синхронизации шаблонов нужно было б делать правки полей в нескольких местах и перегенерациию также соотвественно в нескольких местах, а так в админке выставил галку на отгрузку, клиенту шаблон перегенерировал и пользуйся.
Как вариант могу предложить почитать расширеный вариант выступления разработчицы из chucklefish про переезд на Rust и собственно переходу к ECS архитектуре. Там есть кусок про AnyMap.

UFO landed and left these words here

У вас же игровой движок — это по сути отдельная тьюринг машина со своим описаниям мира и мутациями над этим самым миром. Как там можно по-другому?
Неудивительно, что в тех же майнкрафтав на стандартных блоках умудряются вычислители собирать (ну, или на dwarf fortress)

Можно кучу лапши из классов, например. И если это не UE или Unity, то это довольно частое явление. Подсмотрите как делаются игры на каком-нибудь cocos2d-x или love2d, или новеллы на RenPy. Последним ECS редко нужна, например — там от того тьюринга только переключение экранов.

А что это она тьюринг машина? Может это просто лямбда-функция или декартово-закрытая категория (;

UFO landed and left these words here

У меня тоже. Скорее замкнутая, да (=

На хаскеле неплохо пишется ECS без std::any.

А много тех игр на хаскеле? Чтоб с графонием и грабить корованы можно было. Всякие шахматы и крестики-нолики в расчет, соотвественно, не берем. Использование ECS вне контекста игр тоже.

I used to write a lot of FORTRAN
For science it worked flawlessly
Try using it for graphics
Write in C…

Какое количество требуется чтобы аргумент стал валиден?

Хотя бы штук 5 и суммарное количество игроков было хотя бы over 9000. Или хотя бы исходники размером с какой-нибудь battle fo wesnoth

Думаю у MagicCookies уже есть больше 9000 игроков. Плюс три игрушки только я сам написал. Думаю ещё одну можно найти.

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

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


https://github.com/jonascarpay/apecs

Если я правильно прочитал, то вместо any у хаскела стирание типов происходит через Data.Proxy. Какие накладные расходы по памяти/процессору при этом возникают и возникают ли- для меня вопрос.
А вообще предложение почитать, что-то вроде


forall w m c. Set w m c => Entity -> c -> SystemT w m ()

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


А ссылку на магические печеньки все же приложите.

Proxy только один из способов указать компилятору на тип. Я не уверен, что он в рантайме куда-то передаётся т.к. несёт ноль информации.


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

UFO landed and left these words here
UFO landed and left these words here
w — world
c — component

Судя по сигнатуре это процедура навешивания компонента на сущность (в мире, который знает как такие компоненты навешивать).

Шаблоны в C++ это и есть, как сказал бы автор статьи, "адское динамическое говнище". И как раз в C++20 это попытались исправить так называемыми концептами.

Почему динамическое-то? Они же на этапе компиляции мономорфизуются.

UFO landed and left these words here

Тут больше подходит слово утиная типизация, только иногда утка только похожа на утку, а на деле это какой нибудь T1000.

Адское статическое, нет?
Да адское статическое, зато быстро работает после компиляции.

Не понял, как темплейте и динамические типы вообще связаны? Темплейты наоборот — шаг в сторону строгой типизации… Каждый темплейтный класс — это новый тип. И по идее будет проблема, если вы будете их складывать, например, в случае складывания float с int.


template <typename T>
class Summ
{   
    T x; 
 public:
    Summ(T value): x(value) {};
    Summ(): x(0) {};
    Summ operator+(Summ const& rhs) const
    {
        Summ result ;
        result.x +=  rhs.x ;
        return result ;
    }
};

  Summ<int> sum0(0);
  Summ<float> sum1(2.0f);
  sum0 = sum0 + sum1 ; //Такое не проканает

  int sum00(0);
  float sum01(2.0f);
  sum00 = sum00 + sum01 ;  //а такое проканает
Темплейты используются в том числе для реализации обобщенных методов — т.е. для того же, для чего часто нужна динамическая типизация.

Обобщенные, и динамические разные вещи. В обобщенную метод код будет подставлен все равно конкретный, с конкретным типом, где вы не сможете float сложить с int. Там будет либо float, либо int. И да, все это проверится компилятором, во время компиляции.

Это два разных способа решить одну и ту же задачу — создать обобщенный метод.

Не совсем так, если вы сделали условно обобщенный метод, который может считывать из файла float, или int. Откомпилировали программу с только float методом, так как предполагаете, что правильный файл всегда содержит только float и дали ему на вход int — он выдаст ошибку. А в вашем случае, любой файл подай на вход — он его обработает, только вопрос как?

Интересно, сможем ли мы зайти ещё дальше, и повсеместно взяться за формальную верификацию программного обеспечения? Ведь мало проверить соответствия типов — хотя, на мой взгляд, лучше, чем не проверять — в целях повышения надёжности было бы выгодно сделать невалидные состояния невыразимыми, и вообще, формально доказать, что софт работает корректно во всех возможных сценариях. К сожалению, сейчас такое экзотическое удовольствие дороговато.


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

Почему люди не используют формальные методы? Хабр, 2019.


А в F# изобрели type providers. Которые, тем не менее, в продакшене пока встречаются нечасто.


Интересно, сможем ли мы зайти ещё дальше, и повсеместно взяться за формальную верификацию программного обеспечения?

Сможем, когда придёт время:
«Formal methods will never have a significant impact until they can be used by people that don’t understand them» (с) Types And Programming Languages
UFO landed and left these words here
Не думаю, что время, о котором тут говорит Пирс, настанет.

Я не думаю, что оно настанет в обозримом будущем.

Это, ну, как программировать, не имея ни капельки алгоритмического мышления.

К слову, нужен ли какой-то бэкграунд в математике/теории типов чтобы более-менее продуктивно писать на каком-нибудь Идрисе, или можно всё покрыть документацией(или книжкой по языку)?
UFO landed and left these words here
Как там, кстати, поживает SQL для создания отчётов теми же менеджерами?

Вполне нормально живёт, если не требуется высокой производительности или если нет строгих требований к форме. Типовые задачи, вроде управленческого учёта, закрываются готовыми инструментами. Там где нет готовых, колхозят на коленке на SQL или на Excel или даже на R.
Если предположить, что SQL "отменят", то для простого подсчёта данных и фильтрации придется писать программу для чтения файликов.
"Менеджеры" в наше время уж точно такого не пишут.

А в F# изобрели type providers.

Выглядит круто. Аж захотелось на F# перейти.

строгая статическая/динамическая типизация нужна для компилируемых языков, там где код сильно оптимизируется компилятором/JIT. плюсы, сишарпы, ява и т.д.

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

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

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

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

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

Я ни один скрипт не стану писать без типов, если его будет кто-то читать(а его будет, кроме единственного случая, когда я хочу что-то сделать и удалить на локальной машине). В случае PHP хотя бы psalm(костыль, конечно, но что есть).

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

1. Накладные расходы чтобы прописать тип который в голове и так должен быть — серьёзно?
2. Типы нужно поддерживать и в динамическом языке. Поддерживать, проверяя что код рабочий и типы те что ожидаются, и в статически-типизированном языке эту работу за вас сделает компилятор.

Spoiler header
Ну и вообще, есть языки со статической типизацией, с весьма хорошим автовыводом типов, таким что большую часть их объявлений можно стереть. Спойлер — так никто не делает, ибо незачем.
>1. Накладные расходы чтобы прописать тип который в голове и так должен быть — серьёзно?

а зачем? вы приводите к строке то, что вам пришло из $_POST['fieldname']?
в таком случае просто типизации мало, нужно еще очищать пользовательский ввод от потенциально опасных данных (инъекции, xss) и ограничивать его по длине.
Т.е. типизация это всего лишь один из инструментов для гарантии правильной работы и даже он не дает всех гарантий.

например вам пришло $_POST['number'] в пользовательском вводе, которое вы счастливо приводите к int. Потом делите на него и потенциально получите либо деление на отрицательное число (что может не соотв бизнес логике) либо на ноль (что вызовет рантайм ошибку).
Т.е. типизация не заменяет то, что данные в динамических языках соотв. логике, надо следить чтобы данные были в корректных диапазонах и т.д.

если вы хотите городить иерархии классов на каждое поле в бизнес логике это просто отнимает время и раздувает код как в яве. Эти фактори. которые порождают фактори, которые используют билдеры, которые порождают фактори и так на 20 уровней вниз по стеку
UFO landed and left these words here
просто пример, если мы говорим про примитивные структуры данных типа string, float, int то они не гарантируют корректность бизнес логики. Нужно еще поверх типизации, например кастования к int еще и проверять на корректный диапазон значений (чтобы не было отрицательного кол-ва товаров в корзине например).

Это можно делать как своими классами, так и простыми if/else, но ведь if/else это же не типизация?

динамические языки тем и сильны, что помимо системы типов есть другие вещи которые гарантируют корректность структур данных, например те же регулярные выражения — позволяют выразить сложную грамматику на уровне текстов, которая посложнее чем просто приведение к string.
Мне кажется типизация это не панацея, нужна культура кодинга где все данные проверяются на корректность максимально строго — именно это и дает в итоге правильно и безопасно работающие программы
Нужно еще поверх типизации, например кастования к int еще и проверять на корректный диапазон значений (чтобы не было отрицательного кол-ва товаров в корзине например).

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

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

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

А с чего вы решили что регулярные выражения существуют только в языках с динамической типизацией?
UFO landed and left these words here
Мне кажется, или вы просто выносите проверки из логики в типы, где точно так же можно накосячить?
Накосячить можно везде. Идея типов в том, что вы «запоминаете» эту самую проверку и точно знаете, что если в функцию прилетает Int > 0, то там вообще никак не будет нуля или меньше нуля. Вам не надо смотреть все вызовы этой функции, не надо проверять самому внутри, etc.
Тут подвох в другом. Это выглядит очевидным лишь для простых случаев. Развивая идею дальше, вводя чуть более сложные теоремы и их доказательства, вы упретесь в интересную математику.

Например, пусть в функцию суммирования прилетают два числа с ограничением 0 < Int < INT_MAX & Int <> 42. Что будет корректным типом для результата? Чтобы вывести, компилятор должен знать кое-что о целых числах, свойствах операции сложения и, возможно, переполнении.

Реальный код, как правило, будет еще чуть более сложным, чем эта функция сложения. Даже если математика сойдется, то вы вряд-ли будете довольным временем, которое тратится на компиляцию.
Ну так сделайте не «два числа с ограничением», а «два разных численных типа данных» и определите для каждого из них «функцию суммирования». И если вам обязательно по какой-то причине суммировать их между собой, то ещё и для этого свою «функцию суммирования».

И что-то я сомневаюсь что в языке с динамической типизацией вы найдёте решение проблемы которое будет сильно проще. Ну или как вы там будете складывать два таких числа и что получите в результате?
Написать можно что угодно на чем угодно, только нужно-ли? Поэтому одно из возможных решений — не создавать себе проблемы. Но если интересно заморочиться, то зав.типы в последнее время опять активно копают. Думаю 0xd34df00d про это лучше расскажет.
UFO landed and left these words here

Просто опердени и лендинги не нужны.

Ну так это вы сначала определитесь нужно или не нужно. Вы описали проблему и я написал вам возможный вариант её решения. Если это для вас не проблема и решение вам не нужно, то о чём был ваш комментарий?

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

Если у вас такие проблемы встречаются часто, то вы можете перейти на язык со статической типизацией и они будут решаемы. Или вы можете показать мне ваши примеры кода из языка с динамической типизацией, где вы как-то по другому решаете эти проблемы. Более элегантно. Или более перформантно. Или ещё как-то по другому, но «лучше» чем мой вариант.

Или у вас таких проблем тоже нет и вы их просто решили высосать из пальца в попытке придумать пример с которым не справится язык со статической типизацией? Или как понимать вот этот ваш комментарий?
UFO landed and left these words here
Тот факт, что некоторые типы на данный момент нельзя полноценно прописать или они требуют овер 9000 ресурсов не значит, что статик типизация не нужна. Иными словами, если вы не можете статически проверить всё, это не значит, что не надо проверять вообще ничего.

Насчёт примеров и простых случаев — в языке, на котором я пишу по работе, отсутствует Option/Maybe и почти все типы по умолчанию nullable. Сделать Optional на уровне типов — примитивщина, в разы проще тех же dependent types, но профит от неё огромен.
Собственно, мой поинт — у статик типизации есть sweet spots, где она не выливается в необходимость писать математические пруфы и позволяет реально улучшить качество и надёжность кода. Вполне возможно, со временем эта планка будет меняться и те же не отрицательные числа на уровне типов будут мейнстримом.
UFO landed and left these words here
Впереди этой математики неизбежно маячит теорема Гёделя. То есть, общим решением это никогда не станет.
UFO landed and left these words here
Тогда неизбежно возникает следующий вопрос: будет ли то, что разрешимо, хоть сколько-то полезно практически.
UFO landed and left these words here
UFO landed and left these words here
Накосячить в объявлении типа я имею в виду.
UFO landed and left these words here

вопрос из зала — эти проверки не приводят к долгому времени компиляции?

UFO landed and left these words here

Тайпчекинг на compute shaders! Ну а чо, в постгрю же засунули.

динамические языки тем и сильны, что помимо системы типов есть другие вещи которые гарантируют корректность структур данных

Простите, я искренне не понял — а в статических языках эти «другие вещи» отсутствуют?
Проблема, которую я лично вижу в статик типизации — слабые системы типов не всегда позволяют развернуться и приходится городить очень много кода. Однако, это не делает плохой статик типизацию per se.
присутствуют, но в виде уродливых темплейтов или менее уродливых дженериков.

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

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

Какие именно «другие вещи гарантирующие корректность структур данных» вы имеете ввиду? Конкретный пример можно?

Ну и желательно чтобы они были «не уродливее дженериков». Ну и неплохо было бы ещё увидеть объяснение в чём конкретно заключается и измеряется эта самая «уродливость».
Ага, а потом оказывается что в программе полно дыр для SQL injection. В С++ со строгими типами мне не удаётся представить, как надо извратиться, чтобы такую дыру создать.
проблема которую я вижу — в статической типизации — нужно перекомпилировать программу

Зависит от области, конечно, но в вебе, с которым я работаю, это не вызывает проблем. В чём именно ишью с компиляцией программы?
Код проглотит любой тип которые ему дали

Приведите пример такого кода и таких проверок, плз.
UFO landed and left these words here
UFO landed and left these words here
Поэтому разработка двух компонентов на JS, по 9 за штуку, в сумме стоит 99.
UFO landed and left these words here
в простейшем случае истинный js программист напишет что-то типа:
if (+a > +b) {
}
и всё будет нормально сравниваться :)
Ну, конечно, если там ожидаются либо строки с цифрами унутре, либо просто цифры.
Что весьма похоже на объявление типа. А если понадобится целое напишет a|0. А если строка — a + "".

А если понадобится вектор к примеру, то придется писать тесты «что будет если передать вместо вектора улыбающуюся какашку»
яваскрипт это вообще один большой WTF гляньте здесь например javascriptwtf.com и скажите как ЭТО может жить совместно с типизацией?
Вот у меня в проекте объём Lua-кода 6,3Мб.
Причём написан многими людьми за пять лет.
Мне очень, очень хочется получить статическую типизацию для него, потому что сейчас каждое изменение кода это шаг в неизвестность. Я пристально гляжу на Haxe (у него есть целевая платформа lua), но пока не уверен, что переход себя оправдает.

Правильно говорить динамическая типизация в JS — говно. А не просто динамическая типизация — говно.


А так да, правильно, к одному ЯП надо прикрутить другой ЯП который бы проверял что прога написанная на первом ЯП -ок. Ну а потом еще один который бы проверил что то что проверяет второй ЯП это именно то что надо проверять а не что то другое. Ну а потом четвертый который всё то же самое сделает для третьего.


Система типов это фактически ЯП внутри ЯП. А что если проверять что то что написано на ЯП тем же ЯП? Получится TDD и отсутствие новых сущностей. Что есть гуд. А если программисту (т.е. нежелезному болвану) нужны какие то там подсказки к IDE — ну прикрутите их сбоку! Железный болван и без них сделает все что от него просят.

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

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

Задача:
С порносайта сайта открытой библиотеки скачать все сочинения Ленина. Все кнопки скачивания помечены классом .button, везде в onclick заинлайнен обработчик.
Решение:


Array.from(document.querySelectorAll('.button'))
    .map(a=>a.onclick.toString().match(/https:\/\/[^']+/)[0])
    .join('\n')

Полученный список url'ов скармливаем скриптику:


xargs -a library-lenin.urls -L1 -P8 wget

Задача:
Посчитать время, потраченное на разработку нескольких формочек
Решение:


a = `5:11 2:41 1:31 3:8 1:6 2:31`.split(' ').map(a=>a.split(':').map(Number))
h = a.reduce((s,a) => s + a[0], 0)
m = a.reduce((s,a) => s + a[1], 0)
h += (m / 60) | 0 // float to int **magic**
m %= 60
console.log(`${h}:${m}`)

Задача:
Полоучить строку из ASCII кодов.
Решение:


[97,67,101,123,114,99,84,84,101,104,95,116,48,121,125,116,53,52,115]
    .map(a => String.fromCharCode(a))
    .join("")

PS. Под рукой всегда открыт браузер. И задачки решаются в одну строчку. На питоне их решать надо в несколько...

Задача:
С порносайта сайта открытой библиотеки скачать все сочинения Ленина. Все кнопки скачивания помечены классом .button, везде в onclick заинлайнен обработчик.

Тут показаны преимущества готового API в браузере, а не динамической типизации как таковой. Но удобно, да.


С остальными примерами, извините, не убедили:


Задача:
Посчитать время, потраченное на разработку нескольких формочек

Haskell:


totalTime s = (hours + minutes / 60, minutes % 60)
    where
        parsed = map (map read . splitOn ":") . unwords $ s
        hours = sum . map head $ parsed
        minutes = sum . map (!! 1) $ parsed

main = putStrLn . formatted $ totalTime "5:11 2:41 1:31 3:8 1:6 2:31"
    where formatted (h, m) = show h ++ ":" ++ show m

Rust:


fn main() {
    let parsed: Vec<Vec<u32>> = "5:11 2:41 1:31 3:8 1:6 2:31"
        .split(' ')
        .map(|s| s.split(':').map(|n| n.parse().unwrap()).collect())
        .collect();
    let mut h = parsed.iter().map(|nums| nums[0]).sum::<u32>();
    let mut m = parsed.iter().map(|nums| nums[1]).sum::<u32>();
    h += m / 60;
    m %= 60;
    println!("{}:{}", h, m);
}

Задача:
Полоучить строку из ASCII кодов.

Haskell:


main = putStrLn . map toEnum $ [97,67,101,123,114,99,84,84,101,104,95,116,48,121,125,116,53,52,115]

Rust:


fn main() {
    let s = [97,67,101,123,114,99,84,84,101,104,95,116,48,121,125,116,53,52,115]
        .iter()
        .map(|&n: &u8| n as char)
        .collect::<String>();
    println!("{}", s);
}

Я бы не сказал, что это сильно сложнее.

С экспрессивностью и удобством хаскеля для такого рода задач я спорить не буду, прекрасный язык)


Тут показаны преимущества готового API в браузере, а не динамической типизации как токовой.

Позвольте, .onclick, как и .match(..)[0] могут вполне неиллюзорно выкинуть undefined, и писал бы я на ts, обязательно пришлось бы его убеждать, что я понимаю что происходит с помощью as. В данной задаче я просто знаю о данных больше, чем о них знает исполнитель. Именно в этом доверии к моим умозаключениям и заключается удобство — я знаю лучше и мне не надо никому это доказывать, тем более глупой машине.


Вы же во всех примерах с растром именно занимаетесь тем, что доказываете что лучше его знаете данные (.parse().unwrap(), sum::\<u32>). А ещё вы напрямую сталкиваетесь с самой системой типов — руками говорите, где u8, потому что другие беззнаковые нельзя просто так скастовать к символу, не встретив угрюмую морду rustc.


Конечно, к этому постоянному доказательству, что ты не верблюд, привыкаешь. Но лично мне легче за 3 секунды написать однострочник, который через следующие 3 секунды канет в лету, без явного объявления, что я хочу отстрелить себе ногу, потому что я знаю что не отстрелю.


Ну и ещё один плюс, почему я привёл примеры на js, а не на питоне например — alt + tab, f12 намного быстрее нажимается, чем подождать пока загрузится текстовый редактор и ещё подождать пока rustc выругается на тебя кучей предложений по тому как код писать красивее и правильнее, хорошо если скомпилирует ещё.

Ну и ещё один плюс, почему я привёл примеры на js, а не на питоне например — alt + tab, f12 намного быстрее нажимается, чем подождать пока загрузится текстовый редактор и ещё подождать пока rustc выругается на тебя кучей предложений по тому как код писать красивее и правильнее, хорошо если скомпилирует ещё.

Я тоже IDE не запускал, я просто play.rust-lang.org открыл.

Ну и я не про ide говорил, а про редактор) Но согласитесь, это ожидание, что ты где-то забыл что map работает со ссылками, или что он не выведет сам collect остаётся даже когда ты достаточно много писал на расте. Эдакое ощущение что сейчас тебя учитель будет ругать за то что в хорошем сочинении таким корявым почерком написал все. Хотя может такое глубинное ощущение испытываю только я при компиляции на любом языке… Зато в динамических получишь как Цезарь без объявления войны нож в спину, а иногда и несколько ножей)


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

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

Позвольте, .onclick, как и .match(..)[0] могут вполне неиллюзорно выкинуть undefined, и писал бы я на ts, обязательно пришлось бы его убеждать, что я понимаю что происходит с помощью as

Если Вы уж так уверены, что там точно нет ни одного undefined/null, то на ts добавится всего 2 символа:
Array.from(document.querySelectorAll('.button'))
    .map(a=>a.onclick!.toString().match(/https:\/\/[^']+/)![0])
    .join('\n')

Хотя гораздо безопаснее все же написать так:
import {fromNullable, andThen, map, unwrapOr} from '@lambda-fn/option';
import {pipe} from 'ramda';
Array.from(document.querySelectorAll('.button'))
    .map(a => pipe(
            andThen(fn => fromNullable(fn.toString().match(/https:\/\/[^']+/))),
            map(matched => matched[0]),
            unwrapOr('')
        )(fromNullable(a.onclick))
    )
    .filter(url => url !== '')
    .join('\n');
UFO landed and left these words here

Реклама хацкеля удалась, зачет )

UFO landed and left these words here

Крутой пример решения, и задачка интересная. Всегда любил такие простые но экспрессивные примеры на хаскеле. К сожалению задача не входит в тот класс задач, что показывал я. Да и система типов тут не очень то и роляет. Тут роняется наличие оператора паттернметчинга (в принципе вполне заменяемого на switch в js) и самое главное генераторов списков.


А можете припомнить время, за которое нужно было 2к-ты элемент найти? Я бы хотел попробовать решить такую задачку.

UFO landed and left these words here
Это типичная олимпиадная задача с подвохом(ещё и не одним), тут не надо супер крутых структур данных. Тут главное понять, какую именно идею подсунул автор. Я хаскель не понимаю, так что сказать что там происходит не могу, но на JS такое можно решить так:
JS
N = 2000;
start_ijk = {
    i: 0,
    j: 0,
    k: 0
};
list = {
    v: start_ijk
};
list_last = list;
nums = {};
nums_count = 0;

function getNum(v) {
    return Math.pow(2, v.i) * Math.pow(3, v.j) * Math.pow(5, v.k);
}

function expand(v) {
    if (nums[getNum(v)] == undefined) {
        list_last.next = {
            v: v
        };
        list_last = list_last.next;
        nums_count++;
    }
    nums[getNum(v)] = true;
}

while (nums_count < N) {
    expand({
        i: list.v.i + 1,
        j: list.v.j,
        k: list.v.k
    });
    expand({
        i: list.v.i,
        j: list.v.j + 1,
        k: list.v.k
    });
    expand({
        i: list.v.i,
        j: list.v.j,
        k: list.v.k + 1
    });
    list = list.next;
}

let max_in_loop = Math.max.apply(null,
    Object.keys(nums).map(Number)
);

while (list) {
    if (getNum(list.v) < max_in_loop) {
        expand({
            i: list.v.i + 1,
            j: list.v.j,
            k: list.v.k
        });
        expand({
            i: list.v.i,
            j: list.v.j + 1,
            k: list.v.k
        });
        expand({
            i: list.v.i,
            j: list.v.j,
            k: list.v.k + 1
        });
    }
    list = list.next;
}

nums_array = Object.keys(nums)
    .map(Number)
    .sort(function(a, b) {
        return a - b;
    });

//console.log(nums_array);
console.log(nums_array[N]);


Работает менее секунды, однако не факт, что решено правильно. Ответ получился 8153726976.

Я бы вместо объектов для проверки решенности воспользовался бы Set, он обещается побыстрее быть особенно на моменте приведения строковых ключей к чиселкам.


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


Ну и ещё тут у вас ошибка с next, который почти всегда будет указывать только на увеличение k.

UFO landed and left these words here
Да ошибся на единицу 2 раза(не добавил 1 элемент и взял на элемент больше, чем надо).
Код исправленный
N = 5000;
start_ijk = {
    i: 0,
    j: 0,
    k: 0
};
list = {
    v: start_ijk
};
list_last = list;
nums = {
    1: true
};
nums_count = 0;

function getNum(v) {
    return Math.pow(2, v.i) * Math.pow(3, v.j) * Math.pow(5, v.k);
}

function expand(v) {
    if (nums[getNum(v)] == undefined) {
        list_last.next = {
            v: v
        };
        list_last = list_last.next;
        nums_count++;
    }
    nums[getNum(v)] = true;
}

while (nums_count < N) {
    expand({
        i: list.v.i + 1,
        j: list.v.j,
        k: list.v.k
    });
    expand({
        i: list.v.i,
        j: list.v.j + 1,
        k: list.v.k
    });
    expand({
        i: list.v.i,
        j: list.v.j,
        k: list.v.k + 1
    });
    list = list.next;
}

let max_in_loop = Math.max.apply(null,
    Object.keys(nums).map(Number)
);

while (list) {
    if (getNum(list.v) < max_in_loop) {
        expand({
            i: list.v.i + 1,
            j: list.v.j,
            k: list.v.k
        });
        expand({
            i: list.v.i,
            j: list.v.j + 1,
            k: list.v.k
        });
        expand({
            i: list.v.i,
            j: list.v.j,
            k: list.v.k + 1
        });
    }
    list = list.next;
}

nums_array = Object.keys(nums)
    .map(Number)
    .sort(function(a, b) {
        return a - b;
    });

//console.log(nums_array);
console.log(nums_array[N - 1]);


Всё так-же меньше секунды. Сколько ест не скажу, в браузере запускал.
Сколько на 5000 оно работает и сколько памяти ест?
А что за проблемы вообще в это задаче? Тут даже Python ест какие-то копейки, пишется за минуту, работает секунду.
N = 5000
numbers = [1]
mt = 1
while len(numbers) < N or numbers[N-1] > mt:
  numbers = sorted(list(set(numbers +
                            [n * 2 for n in numbers] +
                            [n * 3 for n in numbers] +
                            [n * 5 for n in numbers])))
  mt += mt
print numbers[N-1]
И никакой ленивости. Ответ 50837316566580.

P.S. Ленивость, кстати, в Python есть. Но это будут уже монстры как у Druu или samrrr… Зачем?
UFO landed and left these words here

Чет жесть. Это же простой обход графа в ширину:


const next = (lst: number[]) => [...new Set([lst[0] * 2, lst[0] * 3, lst[0] * 5, ...lst])].sort((x, y) => x < y ? -1 : 1).slice(1);
const loop = (lst: number[], i: number): number[] => i === 0 ? lst : loop(next(lst), i - 1);

ну или он же если перформансом упарываться:


function binarySearch(lst: number[], val: number) {
  let m = 0;
  let n = lst.length - 1;
  while (m <= n) {
    const k = (n + m) >> 1;
    if (val > lst[k]) {
      m = k + 1;
    } else if (val < lst[k]) {
      if (k === 0 || lst[k - 1] < val) {
        return k;
      } else {
        n = k - 1;
      }
    } else {
      return k;
    }
  }
  return -m - 1;
}

const insert = (lst: number[], val: number) => {
  let i = binarySearch(lst, val);
  if (i < 0) {
    i = lst.length;
  }

  if (lst[i] !== val) {
    lst.splice(i, 0, val);
  }
};

const next = (lst: number[]) => {
  const fst = lst[0];
  insert(lst, fst * 2n);
  insert(lst, fst * 3n);
  insert(lst, fst * 5n);
  lst.shift();
};

const loop = (i: number) => {
  const lst = [1n];
  while (i > 0) {
    next(lst);
    i--;
  }
  return lst;
};

хз как на js быстрее, там уже рядом начинаются тормоза splice/slice и бигнумов

UFO landed and left these words here

Хз, у жс свои взаимоотношения с массивами. Если на списках переделать (но тогда бинарный поиск работать не будет), то 3n*(средний_бигнум + указатели) выделено и мало в пике (размер списка очень медленно растет, на 1кк он что-то вроде 20к элементов).
Бтв решение samrrr с оценкой как я понимаю допиливается до константы (просто формулой считаем ответ :))

UFO landed and left these words here
Соответственно, это можно считать в сильно сублинейной памяти

Я не про память, я имел ввиду, что в принципе, кажется, можно посчитать нужное число аналитически.

А зачем вам сублинейная память, если у вас там линейная память где-то на 20000 элементов нужна всего?
UFO landed and left these words here
Потому что интересно же решать задачу так, чтобы она малой кровью работала с как можно большими порядками величины!
Совершенно необязательно. Если мне задачу нужно решить один раз и получить ответ, то важным для меня будет время написание + время прогона. И для чисел до нескольких тысяч «наивное» решение достаточно.
В любом случае, это на полтора порядка больше, чем эмпирически необходимые ~30 мегабайт в случае с сублинейной памятью выше.
Согласен. Вопрос только том, как часто такие задачи, удачно ложащиеся на ленивость, возникают на практике.

Платить-то за это приходится всегда, а вот прибыль получить… ну вот Вы, вроде бы, писали что-то реальное на Haskell. Как часто вас ленивость спасала? В реальных задачах, не в задачах с собеседования?
UFO landed and left these words here
В любом случае, это на полтора порядка больше, чем эмпирически необходимые ~30 мегабайт в случае с сублинейной памятью выше.

30мб это сколько элементов списка имеется в виду?


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

Ну тут-то ленивость вообще не при делах.

UFO landed and left these words here
Ну почему же?

А какая там связь с ленивостью? Все то же может быть быть и в энергичном языке. С другой стороны — ленивость языка не гарантирует описанного поведения.

UFO landed and left these words here

Переписал ваш пример для читаемости


function generate(maxValue) {
  const nums = new Set(), vecQueue = [ [0, 0, 0] ]
  let maxInLoop = false

  for(let [i, j, k] of vecQueue) {
    const num = (2 ** i) * (3 ** j) * (5 ** k)

    if(maxInLoop && num > maxInLoop) continue

    if(nums.has(num)) continue
    nums.add(num) 

    if(!maxInLoop && nums.size > maxValue) {
      maxInLoop = Math.max(...nums)
      // = 186264514923095700000
      console.log('Max in loop:', maxInLoop)
      console.log('Queue:', vecQueue) // length: 15_001
    }
    vecQueue.push([ i + 1, j, k])
    vecQueue.push([ i, j + 1, k])
    vecQueue.push([ i, j, k + 1])
  }
  console.log('Full queue: ', vecQueue) // length: 46_162
  return Array.from(nums)
    .sort((a, b) => a - b)
  //       .slice(0, maxValue)
}

console.time('test')

var values = generate(5000)

console.assert(values[1999] === 8062156800)
console.assert(values[4999] === 50837316566580)
console.log('Values:', values) // length: 15_387

console.timeEnd('test')

0xd34df00d, время выполнения ~150ms, количество всех объектов подписано в коде. Вывод Node.js по памяти:


RSS: 22.6 MB (22634496)
HeapTotal: 12.9 MB (12890112)
HeapUsed: 5.9 MB (5918300)
External: 856.7 kB (856689)
Будет ещё быстрее если убрать лишние console.log

Я под ночь плохо представляю себе этот ряд полностью, но кажется что EQ не будет никогда и это вполне себе dead code.

UFO landed and left these words here

В данном конкретном случае получить число 10 мы можем только вектором 1,0,1 (2^1 3^0 5^1). Может я не очень понимаю что-то про expand, но в такой вектор система может придти только однажды, вроде

2 3 5 взаимно простые числа, значит каждое число имеет однозначное разложение на множители в виде 2^i*3^j*5^k.
2^i*3^j*5^k <=> (i,j,k)
10 <=> (1,0,1)
15 <=> (0,1,1)
Значит разные наборы ijk создадут разные числа и наоборот.

Вот я об этом и думал. Что не просто так такие числа выбраны, а чтобы можно было каждое число только один раз представить.
Но тогда и Set и объект не нужны, потому что всегда разные числа будут.

UFO landed and left these words here
Тут можно по разному обход организовать. И всё упирается в то, что нам нужно. Если побыстрее код написать — то лучше дубликаты убрать. А если в Attiny впихивать — тут да, тут желательно числа без дубликатов порождать…

Но туда рантайм Haskell даже без программы не влезет, так что смысла в Haskell не будет точно.

Не точно. Хаскелем можно писать код, который впихнётся на Attiny.

UFO landed and left these words here

Ivory скорее вывезли, они теперь с copilot играются.


Но я про language-c-quote с ~колхозными~ бизнес-обёртками.

UFO landed and left these words here

Я придумал как решить эту задачку. Решается она все же больше математически чем программированием. Завтра днём покажу. Сразу скажу что Решать собираюсь графом… Ну и решается она к сожалению нифига не за линейное время...

import heapq
import itertools

def sequence(start=1, factors=(2, 3, 5)):
  nums, prev = [start], None
  while True:
    if (current := heapq.heappop(nums)) != prev:
      prev = current
      for k in factors:
        heapq.heappush(nums, current * k)
      yield current

pos = 2001
print(next(itertools.islice(sequence(), pos, pos + 1)))

8 строчек питона против 9 строчек хаскеля, считая саму функцию.


Даже корявая плюсовая реализация


int64_t get_nth(size_t pos) {
    std::priority_queue<int64_t, std::vector<int64_t>, std::greater<int64_t>> nums;
    nums.push(1);
    int64_t prev = -1;

    while (pos) {
        int64_t top = nums.top();
        nums.pop();

        if (top != prev) {
            prev = top;
            for (auto k : {2, 3, 5}) {
                nums.push(top * k);
            }
            pos--;
        }
    }

    return prev;
}

содержит всего лишь 13 осмысленных строчек.


Только вот сам пример не очень, потому что он демонстрирует решение ad-hoc задачи уровня "смотри как могу". В этих 9 строчках недостаточно когнитивной нагрузки чтобы статическая типизация начала помогать лучше понимать код и недостаточно практической применимости чтобы статическая типизация дала измеримый выигрыш в скорости.


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


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


p.s. а статика, конечно, лучше динамики для проектов на 1к+ строк кода.

UFO landed and left these words here
А, хип импортируете…

Тогда можно было бы не писать mergeUniq руками, а взять что-то такое из уже готовых библиотек, и было бы две осмысленных строки хаскеля.
Тут, всё-таки, речь идёт про стандартную библиотеку, против стороннего модуля… но это уже всё к статике/динамике уже совсем никакого отношения не имеет.
2 задача С#:
var a = "5:11 2:41 1:31 3:8 1:6 2:31".Split(' ').Select(x => new int[]{ int.Parse(x.Split(':')[0]) , int.Parse(x.Split(':')[1]) });
var h = a.Sum(e => e[0]);
var m = a.Sum(e => e[1]);
h += m / 60; 
m %= 60; 
Console.WriteLine(h + ":" + m);

И никакой magic.

3 задача C#:
Console.WriteLine(
(new []{97,67,101,123,114,99,84,84,101,104,95,116,48,121,125,116,53,52,115})
.Select(e => (char)e)
.ToArray()
);

.Select(e => (char)e)

Разве Cast<char> не эквивалентен?

Я просто переводил код с JS на C#, дабы показать что наличие статических типов не меняет сути решения(а заодно показал, что в символ превратить цифру в языке со статической типизацией проще).
Увы, нет. Для структур
Cast<TResult>
вообще не работает, тк он внутри фактически делает
return (TResult)(object)x;


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

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

Круто! Ещё бы можно было писать просто parse, с последующим автоматическим выведением типов, как в расте, и все стрелочные функции заменить на выражения с it, как в котлине, и вообще пальчики оближешь)

Ещё бы можно было писать просто parse, с последующим автоматическим выведением типов, как в расте

Не выйдет, система типов в C# для этого не приспособлена совершенно.

Альтернатива 2 задаче (разве что тут вывод времени будет вместе с секундами)
Console.WriteLine("5:11 2:41 1:31 3:8 1:6 2:31".Split(' ').Select(i => TimeSpan.Parse(i)).Aggregate((s, i) => s += i));
Я питонист не настоящий, но последняя задача на питоне решается достаточно красиво (и можно ещё юникод распознать без проблем) в одну строку!
bytearray([97,67,101,123,114,99,84,84,101,104,95,116,48,121,125,116,53,52,115]).decode('ascii')
Kotlin (максимально близко к оригиналу)

Задача:
Получить строку из ASCII кодов.
Решение:
val a = "5:11 2:41 1:31 3:8 1:6 2:31".split(' ').map { it.split(':').map(String::toInt) }
    
var h = a.fold(0) { s, a -> s + a[0] }
var m = a.fold(0) { s, a -> s + a[1] }
	
h += m / 60
m %= 60
    
println("$h:$m")


Чуть упрощенная версия:
var (h, m) = "5:11 2:41 1:31 3:8 1:6 2:31"
    .split(' ')
    .map { it.split(':').map(String::toInt) }
    .reduce { s, a -> listOf(s[0] + a[0], s[1] + a[1]) }

h += m / 60
m %= 60

println("$h:$m")


Задача:
Посчитать время, потраченное на разработку нескольких формочек
Решение:
arrayOf(97,67,101,123,114,99,84,84,101,104,95,116,48,121,125,116,53,52,115)
    .map { it.toChar() }
    .joinToString("")


Количество приведений типов даже меньше (отсутствует float to int **magic** ;) )
Задача для C++
Сделать класс контейнер в который возможно добавлять функции с любым количеством аргументов, после вызывать их по индексу из контейнера.

Например так:
void foo(int a) {}
void bar(std::string str1, const std::string &str2) {}
int main()
{
  list_function_t  list_function;

  list_function.push(foo);
  list_function.push(bar);

  list_function[1]("Hello, ", "world!");

  return 0;
}


UFO landed and left these words here
UFO landed and left these words here
Что делать, если по соответствующему индексу другая функция?

Это хороший вопрос, к сожалению если мы хотим получить return от функции, то место для обработчика ошибок будет занято return`ом. Есть не красивый вариант использовать объект list_function дальше для проверки

list_function.is_error();


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

Но зачем это делать-то?


Одним из случаев может быть сервер/клиентский обмен пакетов с игнорированием сериализации/десериализации, которую мы делаем ручками. Мы просто получим send с множеством аргументов. В данном примере никакой статической проверки типов в рамках C++ невозможно.
Отмечу сразу что существует очень много подводных камней у этого примера.

Приведу еще один хороший пример, кстати последний случай моего использования контейнера функций.

Это просто работа с графом. Перебирать рекурсивно граф не очень удобно, поэтому я перебросил его на коллбек.


typedef graph_t<int> int_graph_t;

void test(int_graph_t *graph)
{
    print_space(graph->level);

	if (graph->is_root)
	{
		printf("is root ");
	}

	printf("%d %d %d parent value: %d parent index: %d\n", graph->level, graph->get_value(), graph->index, graph->parent->get_value(), graph->parent->index);
}

int main()
{
	int_graph_t graph = 0;

	graph.push(100);

	graph.process_function["base"] = test;
	graph.start_process();

    return 0;
}


Если потребуется передать аргумент через start_process() в test(int_graph_t *graph), я просто это сделаю. Это насаживается на классы. Если мне потребуется реализовать новый вид функций, то я легко это сделаю добавив в process_function еще что-то.
UFO landed and left these words here
Динамическая типизация нужна там, где вычисления определяются данными.

Например JavaScript. :) Если смотреть с точки зрения рантайма, то благодаря динамической типизации экономится куча времени на старте программы, которую бы в противном случае приходилось делать браузеру для анализа корректности программы при каждой загрузке скриптов.

С позиции разработчика — формулы в Excel, bash, lua и прочие встраиваемые сценарии — где полезный эффект будет ничтожным по сравнению с трудозатратами на дополнительные церемонии с типами.
Если смотреть с точки зрения рантайма, то благодаря динамической типизации экономится куча времени на старте программы, которую бы в противном случае приходилось делать браузеру для анализа корректности программы при каждой загрузке скриптов.

А представьте, сколько времени можно сэкономить, не проверяя постоянно типы в рантайме!

UFO landed and left these words here
UFO landed and left these words here
экономия на типах не даёт никакого профита.

Угу, и именно поэтому V8 старается перевести JavaScript в типизированный код. Как и LuaJIT. И потому же PyPy выдаёт производительность сильно лучше CPython.

Что-то вы не туда. Вопрос был не про типизацию как таковую, а про динамическую типизацию, когда типы определяются в рантайме. JIT оптимизации используется не только в скриптовых языках, но и во многих компилируемых со статической типизацией — Java, C# и т.п. Это как раз следствие того, что системы типов обладают ограниченной выразительностью, оставляя массу возможностей получать дополнительный выигрыш, анализируя реальные данные.
большинство программ 99.99% времени проводят в ожидании действий пользователя.

Но это не отменяет того класса задач, в которых программы не проводят 99.99% времени в ожидании пользователя.

Более того — даже в тех программах, которые «99.99% времени проводят в ожидании действий пользователя» есть участки, которые-таки работают не в этом режиме — иначе можно было бы заменить ноут с процессором в 4GHz на IBM PC с 4.77Mhz (разница в скорости как раз примерно в десять тысяч раз) — и ничего не заметить.
UFO landed and left these words here

Даже работа этого топика устраивает? :)

UFO landed and left these words here

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

UFO landed and left these words here
А представьте, сколько времени можно сэкономить, не проверяя постоянно типы в рантайме!
Проверка типов на старте программы это дополнительные секунды, когда пользователь результат не видит. Есть ненулевая вероятность, что большая часть поанализированного кода вообще не будет востребована на странице. В вебе идет борьба за микросекунды, поэтому все что можно отложить, откладывается до того момента, пока оно реально не понадобится. С дальнейшей оптимизацией хорошо справляется JIT.

Если вам нужен анализ кода и AOT — делайте это на сервере, один раз во время сборки для продакшена. Тащить все вот это в браузеры ваших пользователей нет ни какого смысла.
В вебе идет борьба за микросекунды, поэтому все что можно отложить, откладывается до того момента, пока оно реально не понадобится.
Только какие-то не за те микросекунды там борются. Потому что перед тем, как на страничке появится первая буква, зачастую исполняется кода больше чем в каком-нибудь Turbo Pascal 7.0 в принципе. А это, так-то, была цела интегрированная среда со встроенным компилятором и дебаггером.

Тащить все вот это в браузеры ваших пользователей нет ни какого смысла.
Ага. Зато 100 копий библиотек — тащить имеет смысл. Современный Web — это самое продорливое и тормозное изобретение человечества. У него много достоинств, но малое потребление ресурсов и отзывчивость — это не сюда.
Инженерные задачи от абстрактно логических отличаются там, что решаются всегда в ограничениях. Веб работает начиная с допотопных телефонов, телевизоров и вплоть до мощных ПК. Наши бекендеры если время поджимает то и дело норовят перетащить куски логики на фронтенд потому, что тут разрабатывать в разы проще и быстрее.
Веб работает начиная с допотопных телефонов, телевизоров и вплоть до мощных ПК.
Вы пробовали хотя бы вот эту вот статью открыть на «допотомном телефоне» или «телевизоре»? Попробуйте. У меня где-то Nintendo DS есть с модулем Opera, если что.

Наши бекендеры если время поджимает то и дело норовят перетащить куски логики на фронтенд потому, что тут разрабатывать в разы проще и быстрее.
И это назвается «мы боремся за миллисекунды» и «допотопные телефоны»?

Не смешите мои тапочки: эпоха «лёгкого веба», который действительно стремился экономить ресурсы (потому что на сервере был мощный SGI или Sun, а на клиентах мог и 80386й оказаться с парой мегабайт памяти), давно прошла.

Сегодняшний веб транжирит ресурсы просто чудовищно и решает тривиальные задачи криво и плохо. Какой-нибубь «порхающий листик» (типа того, что в Windows 95) тормозит на компьютере с несколькими ядрами и гигабайтами памяти. Про «допотопных телефоны» и «телевизоры» лучше вообще помолчать…

Инженерные задачи от абстрактно логических отличаются там, что решаются всегда в ограничениях.
Совершенно верно. Только нужно понимать какие именно ограничения у вас имеются и как вы с ними боретесь. И эти ограничения — это нифига не «допотопные телефоны» (про них уже все забыли давно и никакой веб там давно не работает) и не «миллисекунды» (если бы они были важны — то решения были бы тоже совсем другими).

Ограничение — это отсуствие качественного, грамотного, персонала, способного, как раз, сделать что-то работающее на допотопных телефонах (привет, Java ME) и «экономящих миллисекунды» (для этого есть C++, Rust, в некоторых случаях, возможно, Haskell, но ни javaScript с Babel'ем, ни Web вообще для этого не предназначены). Про WML, который действительно пытался что-то в этом направлении сделать, все уже давно забыли (да и негодная это была попытка изначально, если честно).

Такая потребность была всегда — и решение было тоже всегда (если вы, конечно, не аксакал, помнящий эпоху до JOSS).

Посмотрите на статьи про ретро-компьютеры: на Микроше был бейсик, на БК — ФОКАЛ.

Но никто нигде не пытался заявить, что они используют эти инструменты ради «экономии миллисекунд». Все понимали, что даже если программа, написанная на скомпилированном языке, будет запускаться чуть больше (на пару секунд даже) — это всё равно будет лучше для пользователя.

Я сам лично писал верификатор, который обрабатывал 150-250MB в секунду. Если у вас кода — несколько мегабайт, то он отрабатывает, с точки зрения пользователя, многовенно. А если уж вы грузите на клиента сотню мегабайт (неважно каких — JS или скомпилированного кода), то вас уж совершенно точно не будут волновать те 1-2 секунды, которые ваша программа стартует…

Бэйсик для Микроши позволял напрямую раотать с памятью и вызывать подпрограммы по адресу. Эти возможности часто использовались для "экономии миллисекунд" в том числе ассемблерными (вернее прямо в машкодах) вставками

Совершенно верно. И сегодня тоже можно использовать массу разных… странных технологий, для этого же самого. Всякие типизированные массивы и прочее.

Но это всё — уже следствие изначального выбора. Знаете, я регулярно общаюсь с людьми, которые «оптимизируют» свои решения на PHP, Python, да даже javaScript (хотя там сегодня уже реально можно делать вещи, которые будут «тормозными», а не «дико тормозными»). И, как правило, после того, как удаётся-таки получить реальные числа получается так: они подставляются в табличку, даётся прикидка «на пальцах» и «достижение», в 9 случаях из 10, превращается примерно в следующее: «мы смогли организовать умную схему базы и систему кеширования и теперь наш 64-ядерный сервер с SSD держит такую же нагрузку, как какой-нибудь одноядерный Pentium!!! на 1GHz начала века с HDD».

После чего остаётся только переспросить: «А вы точно уверены, что в вашей архитектуре главное — это „масштабируемость“, „эффективность“ и другие модные слова? Или, может быть, всё-таки зарплата людей, которых вы нанимаете на работу — важнее?».

И, заметьте, в ответе «да, нам нужны дешёвые и, соотвественно, тупые, программисты» я не вижу ровным счётом ничего зазарного: это — нормальный бизнес-подход. Нанять «таджиков» для копки погреба под три кадки с огурцами — вполне нормально. Но не нужно после этого заявлять, что два вас самое главное — соблюдение параметров проекта и санитарных норм. Не для этого вы «таджиков» нанимали, ведь согласитесь?

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

С зарплатой мне как-то сложно судить. По опыту (PHP, JS/TS) с ФОТ порядка 5000$ нанять можно самых разных программистов. Одни шаг влево-вправо от любимого фреймворка и загребаются на несколько дней, другие плюют на фреймворк и пишут 10 строк "ванильных"

Это как раз ситуация неидеальности рынка. И да, такое «имеет место быть». но принципиально картину не меняет: люди, способные освоить статически типизированные и функциональные языки, в среднем, стоят больше.

Незначительно больше, не настолько чтобы мотивировать меня переключиться с PHP на Java из-за денег.

Но, возможно, достаточно для того, чтобы бизнес выбрал PHP, а не Java для экономии ФОТ.
Ну вот сейчас по быстрому загуглил ситуацию у нас. 70000 на Java против 58500 на PHP. Я бы не назвал это «незначительно больше».

java PHP
5% разницы и это сначала надо будет год джуном, год мидлом — долго будет окупаться проседание

Интересно почему ситуация у нас и у вас так отличается в этом плане.
Потому что на аутсорс модули, которые могут обрушить вам вас весь бизнес, стараются не отдавать, а если вы изначально «пилите» компонент, качество которого для вас не очень важно, то глупо переплачивать за качество.

Сложно сказать. Может у вас на Java пишут только вещи, которые на PHP написать сложно, а то и практически невозможно, а у нас то же что на PHP большей частью. Я вот у наших джавистов то, что видел на Sзring MVC кажется — большей частью можно копипастить в Symfony, убрать типы, синтаксис немного поправить и должно работать.

Вот, кстати, забавно, да. И PHP и Java по сложности практически одинаковы (кроме многопоточности, но, справедливости ради, отсобеседовав порядочно джавистов претендовавших на синьёра с большими деньгами, знания в этой области были даже не у каждого второго).
Да, вход в PHP попроще и наговногкодить там легче. Но если смотреть на задачи и требуемое качество от мидла и выше, то разница нивелируется. Мало того, дейстивтельно сложные задачи на джаве решаются легче и требуют меньше знаний. Т.е. синьёр на пыхе зачастую знает свой язык лучше, чем синьёр. на джаве. А зарплаты ровно наоборот. Мистика.

Звучит так, что изучить мне многопоточность и синтаксис, и можно на java сеньора подаваться :)


А с зарплатами может быть дело в том, что Java это не только веб, но и десктоп с мобайлом (не знаю на встраиваемых она ещё применятся или нет) и хорошо спеиалиста переманить больше желающих.

Звучит так, что изучить мне многопоточность и синтаксис, и можно на java сеньора подаваться :)

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

Вот, кстати, забавно, да. И PHP и Java по сложности практически одинаковы (кроме многопоточности)


Ну, как уже написали выше, Java это ещё и куча десктоп-приложений и там есть своя специфика. Плюс многопоточность это не такая уж и мелочь.

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

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

И если например сравнивать в этом плане Java и C#, то сами по себе языки более-менее одинаковы. Но вот если мы возьмём имеющийся тулинг, количество имеющихся open source пакетов и их доступность/простоту использования, то я бы сказал что C# всё-таки выигрывает.

Даже если целевая платформа Linux? Я знаю про .Net Core, но вот насколько тулинг и библиотеки представлены? GUI есть десктопный?

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

И как бы .Net Core вроде как бы и под линуксом работаeт. Есть та же Avalonia UI например и я ради прикола дома запускал Avalonia UI/.Net Core 3 приложения на линуксе. Но пока это всё ещё очень сыровато. И как минимум нам тогда проще на джаве сделать.

Ну вот именно Core работает вроде уже нормально. А вот насколько вся экосистема готова...

Core даже на винде до сих пор имеет свои «детские болезни». То есть в общем и целом если делаешь всё как в примерах от Майкрософта, то всё работает великолепно. Но как только шаг вправо, шаг влево… То есть не смертельно, решения рано или поздно находятся и баги в пакетах/фреймворках фиксятся относительно оперативно, но проблемки есть.

И лично я сейчас однозначно не буду делать что-то в продакт на .Net Core под линукс. Особенно учитывая что в этом-следующем году будет .Net 5 и там опять всё может поменятся. Ну или точнее я надеюсь что там многое поменяется в лучшую сторону :)

Спасибо. А то уже начал задумываться, может c# надо было изучать для общего развития, а не Java

дешёвые и, соотвественно, тупые, программисты
По-моему, не стоит ставить знак равенства. Даже в одной и той же сфере с одинаковыми технологиями нет равенства из-за личных особенностей. В разных сферах будет играть всё больше рыночный фактор относительно используемых технологий и самой области применения.
В разных сферах будет играть всё больше рыночный фактор относительно используемых технологий и самой области применения.
Собственно именно наличие рынка и должно приводить к тому, что дешёвые программисты будут тупыми. Рассчитывать на высококвалифицированных фанатов своего дела, готорых работать «за похлёбку риса» — неконструктивно.
Рассчитывать на высококвалифицированных фанатов своего дела, готорых работать «за похлёбку риса» — неконструктивно.


Тем не менее, весь рынок труда в РФ и других странах СНГ в 90-е — 2000-х именно так и был устроен. Да и сейчас ситуация изменилась разве что в больших городах.
UFO landed and left these words here

Забавно, у меня на куда более слабом железе и Firefox последней версии всё нормально работает

Забавно другое: то, что во-первых сделать так, чтобы вот это вот всё не тормозило можно (я видел комп на котором книжки, сравнимые по объёму в MS Office 97 набирали — там Pentium 233MMx был и памяти 32MiB), однако этим никто не занимается — и при этом в комментариях регулярно расказывают про качество JS, TS и экономии миллисекунд.

При этом, так-то, Chrome ничуть не менее популярен, чем Firefox, «отмазаться» тем, что разработчики про него не знали не получится.

У вас нет «когнитивного диссонансса» во всей этой истории?

У меня, как ни странно, нет претензий ни к писателям на JS, ни даже к владельцам Хабрахабра: у них другие заботы, экономить чужие ресурсы они ни разу не обязаны.

Но ради бога, если вы «специалист по дендрофекальному методу строительства», то не рассказывайте никому о том, что вы выбираете этот метод за качество результата и можете построить хоть многокилометровый мост, хоть телебашню.
UFO landed and left these words here
UFO landed and left these words here
Динамическую типизацию удобнее применять если ты пишешь говнокод, который должен быть запущен один раз в жизни и удален.
Ага у меня есть такой samrrr.github.io. Но вот только когда код пишется более чем 1 человеком…
А вот еще для автора хорошая тема: Линукс против Винды. Тоже неплохо разжигает эмоции.
Скучно, уже три темы было за месяц. В итоге первая свелась к тому, что либо хром фуу, либо на хабре надо что-то делать со скриптами, т.к. адово тормозит у хромоюзеров, но не тормозит в огнелисе.
П.С. А ведь скрипты на хабре на .js =)
console.time(); $(':focus'); console.timeEnd()

На этой странице выполняется 130мс. На каждое нажатие клавиши.
Boomburum пользователи страдают.

Типы — это хорошо, люблю их и без них никакой серьезной разработки не вижу. Но не трожьте мою возможность говнокодить быстрые прототипы без лишних заморочек! Чтобы хорошо описать типы, порой, нужно СНАЧАЛА увидеть каким получается код. И язык тут — дело второстепенное, с тем-же JS можно прекрасно юзать статический анализ и тайп-линтинг с аннотациями в JSDoc. Типы — это вопрос архитектурный и пылать праведным хейтом стоит, разве что, в адрес плохой архитектуры но никак не стека и самой возможности использовать динамическую типизацию.
И язык тут — дело второстепенное, с тем-же JS можно прекрасно юзать статический анализ и тайп-линтинг с аннотациями в JSDoc. Типы — это вопрос архитектурный и пылать праведным хейтом стоит, разве что, в адрес плохой архитектуры но никак не стека и самой возможности использовать динамическую типизацию.

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

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

Ну вот как-то в PHP нормально относятся к phpdoc аннотациям типа.
phpdoc-аннотации типов в типичном пхп проекте это аннотацими сгенерированные phpstorm'ом которые в лучшем случае не забывают иногда поправлять.

Используют хотя бы статический анализ на уровне psalm/phpstan еденицы.

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

Нет второстепенное. Ваши личные эмоции тут не аргумент. Скриптовые и компилируемые языки — это очень разные вещи априори, и именно этот параметр определяет «костыльность» в данном контексте. В компилируемом языке вынос проверки типов в отдельный инструмент будет костылем, в скриптовом — нет. Как к этому относится какая-то ненавистная вам часть комьюнити — совершенно не важно, более того, скепсис к типам — это ваша личная выдумка, напротив, все больше разработчиков начинает использовать подобные инструменты в обязательном порядке.
Как к этому относится какая-то ненавистная вам часть комьюнити — совершенно не важно
Это важно как минимум потому, что это самое комьюнити пишет код, с которым придётся взаимодействовать, и типы в нем либо будут указаны, либо нет.

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

Во вторых, если язык не поддерживает синтаксис для типов и всего что с ними связано, появляются сомнительные вещи в виде указывания типов в комментариях, а потом появляются эти комментарии в разных форматах для разных анализаторов.

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

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

Нет, это вздор хотя бы потому что мы говорим о разработчиках которые уже выбрали динамические языки. Линтеры и подсказки IDE это не подобные инструменты.

Ваши личные эмоции тут не аргумент.

Аргументы вы почему то решили «не заметить».
Еще раз: в скриптовых языках дистрибуция происходит на более раннем этапе чем обработка в среде исполнения. Поэтому проверка типов и должна производится отдельно, ДО компиляции. Если говорить о веб-платформе — то там еще и большая сегментация самих сред, что добавляет.
Аргументы вы почему то решили «не заметить».

И я до сих пор их не вижу. «будет костылём», «рядом не стоят» — это все ваши эмоции.
Еще раз: в скриптовых языках дистрибуция происходит на более раннем этапе чем обработка в среде исполнения.

Это никак не оправдывает неудобство от динамики и не делает её удобнее.

И я до сих пор их не вижу. «будет костылём», «рядом не стоят» — это все ваши эмоции.

Тогда попробуйте наконец прочитать мой предыдущий комментарий полностью.

А то что вы ставите статическую типизацию в один ряд с необязательными линтерами для js'а говорит лишь о том что вы не понимаете о чём пишете.

Поэтому проверка типов и должна производится отдельно, ДО компиляции. Если говорить о веб-платформе — то там еще и большая сегментация самих сред, что добавляет

Тайпскрипт и другие компилирующиеся в js языки вполне себе показывают что веб-платформа и компиляция с тайп-чекером могут успешно сосуществовать.

Более того, очень популярная ныне компиляция js в js это показывает :)

Raku aka Perl6 поддерживает опциональное объявление типов.
Тем не менее, например в C# (начиная с 4-й версии), добавили динамическую типизацию, что продиктовано скорее необходимостью и решает определенные задачи при разработке. Автор немного преуменьшает роль дин. типизации.
Ну в C# и object всегда был. И да, определённые задачи они решать помогают. Но это скорее проблемы, которые возникают когда выходишь за пределы «контекста».

Например когда работаешь с рефлексиями(что само по себе тоже не особо рекомендуется) или скажем с СОМ-объектами или c результатами динамических LINQ-запросов.

Но никому же не придёт в голову весь свой С# код перевести на динамическую типизацию.

ЕМНИП в .NET её завезли для более удобных поддержки динамически-типизированных языков и работы с COM'ом. Т.е. никакую отдельную задачу в шарпе dynamic не решал, а нужен был для интеропа.

В платформу .NET могли и завести, а в язык C# зачем?

Не совсем понимаю что вы хотите сказать. В С# её добавили чтобы облегчить работу с этими самыми COM-объектами. И она эту работу действительно облегчает.

Не сосем понимаю вопроса. Почему я сказал .NET а не C#? Не знаю. Может потому что dynamic добавили на уровне платформы, а не только языка.

Видимо неправильно понял без вашего контекста. Для меня звучит типа: платформе нужна была поддержка динамически типизированных языков и COM, поэтому добавили динамический тип. Ну и между делом в C#, раз уж в платформе уже есть. Для меня COM и прочие OLE только с C/C++ ассоциируются, что на C# кому-то с ними может понадбиться работать как-то в голову не приходило, хотя казалось бы...

Не знаю как сейчас, а раньше например считай всё взаимодействие с майкрософтовским офисом в С# шло через COM. И это было то ещё удовольствие…

Ну и иногда действительно приходится например из С# работать с С/С++ библиотеками. Тоже не то чтобы от хорошей жизни, но иногда деваться некуда.

Я с ним взаимодействовал в те времена, когда .NET был какой-то новой штукой непонятно зачем нужной, когда есть Visual Studio C++ 6

Работа с интеропом одна из задач, но правильнее было бы сказать, что решаются задачи удобного взаимодействие с другими средами исполнения или API-интерфейсами (интероп часть этих определений).

Не подумайте, что «удобство» в данном смысле заменяет «правильно». Любое взаимодействие нуждается в проверке (и типизации, чаще всего).
UFO landed and left these words here

Медленные тулы, потому что написаны на JS, а он к сожалению пока проигрывает компилируемым языкам. А "нормальные IDE для js" медленная только одна — WebStorm, потому что написана на Java, VSCode причём по-шустрее будет, и написан он на js (ts).


В общем, да, задумывались и знаем почему, удивляться тут нечему.

Я думаю тулы настолько медленные не столько от языка, а от того как они написаны.

Программа на С++ будет работать быстрее чем на JS примерно в 5-10 раз. Я не думаю, что то как тул написан повлияет на производительность так-же сильно.

От программы зависит. Как минимум V8 активно использует JIT c оптимизацией по типам.

Часто все упирается в ассимптотическую сложность, во всяких сложных тулах это особенно часто бывает.
Грубо говоря, неправильный алгоритм даст не в 10 раз просадку, а во все 10^n раз.
А если задача не cpu-bound, то основные тормоза как раз работа с io дает.

JS всегда будет проигрывать компилируемым языкам, так как для выполнения a+b процессору всегда придётся выполнять проверку типов. В языке с статической типизацией просто будет сложение.

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

Причём тут JIT? Проблема в динамической типизации а не в JIT. Если типы в JS станут выводиться статически, то JS станет TS. А TS, если очень постараться может и до с++ дотянуть(когда-нибудь). WebAssembly как раз к этому и идёт.
Сейчас же function f(a,b){return a+b;} в JS не оптимизируешь.
JIT тут притом, что если ваша f(a,b) запускается 1 раз — её оптимизировать не нужно. Если миллионы раз со значениями одного типа — то jit её скомпилирует до скорости нативного кода.

Нет, совсем проверку типов он не сможет выкинуть, то есть он может запустить по быстрой ветке, но ему никто не дает гарантий, что в каком-то случае вместо int не прилетит string

С трудом представляю ситуацию, когда в теле цикла, которое выполняется много тысяч раз, вдруг после кучи итераций сменился тип значения переменной. Можете привести реалистичный пример?

Любой вызов внешней функции из такого цикла

Что вы имеете в виду под «внешней функцией»? Цикл типа такого:
f(i) = i^2

res = 0
for i in 1:1000
    res += f(i)
end

должен без проблем оптимизироваться нормальным JIT.

Любая функция, ходящая через границы сред исполнения, которую невозможно заинлайнить, ну например какие-нибудь built-in браузерные функции или любые нативные вызовы из nodejs.

Так речь изначально шла про оптимизацию JIT vs статическим компилятором. Как компилятор вам поможет такую функцию оптимизировать, если для него она тоже внешняя?

Хорошо, а если она возвращает значение, как компилятор может доказать, что оно всегда int например? Никак, вот и обломинго с оптимизацией.

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

Фиксирован, но ведь js не знает об этом, внешняя функция может и не всегда один и тот же тип возвращать, типизация ведь динамическая, а если такая гарантия есть, то тогда мы приходим к типизации статической.

JIT спокойно может использовать тот факт, что если вызывается функцияз из .so библиотеки, то она всегда вернёт одинаковый тип. Причём тут статическая типизация языка, код на котором компилируется jit'ом?

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

Вы же не считаете, например, javascript статически типизированным? Однако ничего не мешает jit'у для этого языка использовать тот факт, что функции из .so библиотек всегда возвращают один и тот же тип.
function f(a,b){return a+b;}
sum=["", 1, " thing ", 12, " things"].reduce(f);

console.log(sum);
Ну так этот пример JIT может оптимизировать ещё и лучше, чем статический компилятор (если он вообще сможет такой код скомпилировать). Тип элементов массива во время компиляции неизвестен вообще (если этот массив не задан прямо в коде, конечно), а jit скомпилирует две реализации функции f — f(string, string) и f(string, int).
а jit скомпилирует две реализации функции f — f(string, string) и f(string, int).

И получатся шаблоны из С++. В таком случае это уже статическая типизация и есть.

Ну так этот пример JIT может оптимизировать ещё и лучше, чем статический компилятор (если он вообще сможет такой код скомпилировать).

С помощью С++ и магии шаблонов скомпилится, и будет работать быстрее. Так как будут проверятся id типов, а не строковые названия типов.
Что, причём здесь воообще шаблоны и C++? Вы привели код на javascript, как я понимаю. Ничего не мешает jit'у после некоторого количества итераций, когда соберётся статистика по типам с которым вызывается f(), скомпилировать в машинный код две версии f() — на дальнейших итерациях будет вызываться одна из них после бинарной проверки число или строка на входе. Статическая типизация в смысле c++ здесь никак не поможет, ведь во время компиляции вообще неизвестно, что в массиве (если он не записан в коде). Шаблоны специализируются именно по compile-time типу, так что они здесь совсем не в тему.
Если jit начнёт создавать варианты одной функции, оптимизированные под конкретные типы, то выйдет тоже-самое что и делают сейчас шаблоны в C++. И это уж точно не позволит соптимизировать лучше, и стать быстрее C++.

Ничего не мешает jit'у после некоторого количества итераций, когда соберётся статистика по типам с которым вызывается f()

Это не позволит обогнать C++, так как в C++ будут заранее созданы эти варианты и не придётся постоянно считать, сколько раз и с какими аргументами вызвана функция.

Статическая типизация в смысле c++ здесь никак не поможет, ведь во время компиляции вообще неизвестно, что в массиве
В массиве всегда будет ограниченный набор типов, не бывает так, чтобы хранилось неизвестно что. Но никто не мешает сделать std::variant по всем типам используемым в массиве.
Про «обогнать с++» речи и не было. Jit-компиляция позволяет _догнать_ производительность с++, а не перегнать.
Если jit начнёт создавать варианты одной функции, оптимизированные под конкретные типы, то выйдет тоже-самое что и делают сейчас шаблоны в C++.

Насколько я знаю с++, шаблоны специализируются только по compile-time типу. Но за всякими новыми добавками после с++11 не слежу, может туда уже полноценную динамику ввели :)
UFO landed and left these words here
Так и пусть нельзя гарантировать во всех случаях — в чём проблема? Никакой компилятор не гарантирует и не может гарантировать, что получаемый машинный код всегда оптимальный. Это не зависит от того, в какой момент производится компиляция. Оптимизация в компиляторах делается таким образом, чтобы на практике в большинстве случаев получался достаточно близкий к оптимальному код.

Не стану высказывать здесь своё мнение. Все знают, что в Erlang динамическая типизация. Но вот (к сожалению не вспомнил, где об этом прочитал), Джо Арстронг, автор Erlang, как-то посетовал, что жалеет, что в Erlang изначально не предусмотрели статическую типизацию, и что был проект по её внедрению в него, но когда он был готов на 95%, оказалось, что оставшиеся 5% реализовать невозможно.


Нашёл другое интервью с Армстронгом https://www.infoq.com/interviews/Erlang-Joe-Armstrong/, где он ратует за статическую типизацию и признаётся в любви к Haskell.

Все существующие системы типов обладают ограниченной выразительностью. Поэтому рано или поздно это становится проблемой (например wiki.c2.com/?ExpressionProblem). В таком случае прагматики выбирают инструмент по задаче, а фанатики идут искать себе новую работу чтобы смешить народ дальше.
UFO landed and left these words here
Я когда написал статью, подумал, что аргументов маловато. Потом подумал, что ты придешь в комменты, и махнул рукой — аргументы и без меня найдут

Тут пожалуй полезно будет потом их собирать в доходчивые F.A.Q. Чтобы была прямо удобная выжимка.

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

Но, кажется для вас эта информация и так известна:
«Внутренности вордовских файлов: просто ужас»
«Привет из мезозоя»
Одни из самых популярных ваших статей, и они прям разжигают) Да, тоньше чем это сделал fillpackart, но принцип-то тот же. И судя по тому, что в эту статью я зашел из топа за сегодня, количеству ее оценок и т.д. — разжигать толще тоже можно, просто запас кармы нужен)
Льщу себя надеждой, что кроме разжигания в моих статьях все-таки было и что-то практически полезное.
Получается, что все кто прочитали — идиоты?
Если люди, которые читают статью целиком прежде чем составлять мнение называются идиотами, то я буду с гордостью носить такую лычку)

Это уж точно лучше, чем быть «серьезным взрослым человеком», составляющим свое Очень Важное Мнение по паре фраз, не читая весь текст)
В условиях информационного взрыва это неизбежно. Читать все подряд не хватит никаких сил.

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

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

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

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

> аргументированное разъяснение с кодом
> писать, что ты идиот

>> аргументированное

И какой вывод для себя тут можно сделать?
Все заморочки делятся на две категории: задачи и научные проблемы. Первые решаются понятным способом. У последних либо нет общего решения, либо есть множество альтернативных.
Так это же вполне себе решаемая проблема.

Какое решение порекомендуете?
UFO landed and left these words here
Expression problem к системе типов вообще ортогональна, и абсолютно прозрачно решена в некоторых уже используемых языках — например смотрите мультиметоды в julia.
Одно из популярных определений проблемы гласит:
The goal is to define a datatype by cases, where one can add new cases to the datatype and new functions over the datatype, without recompiling existing code, and while retaining static type safety (e.g., no casts).
Так, что она напрямую связана со статической типизацией.

Отнюдь, просто типичное решение в динамическом ЯП будет содержать лесенку if-else с instanceof, которые надо синхронно править.

Коммерческая разработка это в первую очередь работа на продуктивность. Если вам со своими подходами нужно по 5 часов думать над типами, когда другими инструментами вопрос решается за 10 минут, то нафик такие подходы.
Если потом приходится 20 часов искать баг в коде, написанном за 10 минут, то нафик такую продуктивность.

К сожалению, разница в том, что 20 часов ты ищешь баг УЖЕ с деньгами за проданное ПО, а вот 5 часов думаешь над типами ЕЩЕ без денег. И это самое бабло решает все.

Это только если баг не вскроется до продажи этого ПО. А если вдруг на показе приложение не откроется только потому, что hours=0 и оно false?
А потом, когда ты 20 часов искал баг УЖЕ с деньгами в кармане, но тут оказалось, что за эти 20 часов простоя у заказчика к твоей компании прилетело требование на ЕЩЁ БОЛЬШЕ дофига денег по неустойке, наверное надо будет задуматься, чтобы писать понятный код.
П.С, У меня вот тут почему-то код на perl не работает, может кто поможет?
$??s:;s:s;;$?::s;;=]=>%-{<-|}<&|`{;;y; -/:-@[-`{-};`-{/" -;;s;;$_;see

Я в своей практике сталкивался только со стартапами, а они по неустойкам не платят, максимум — возвратят деньги.
Я думаю риск довольно разумный — все таки ты держишь деньги в руках, а как оно там дальше пойдёт — вдруг повезёт?)


Кроме того, бэклог по багам есть всегда. Понять что из-за типизации их там на 30% меньше, с учётом того что ты НЕ видишь как оно могло бы быть, для менеджера ( Читай бизнеса ) не представляется возможным.


А вот цена и срок MVP — величина конкретная.

Я конечно извиняюсь, но это подход «маяк-маяк и в продакшен», от которого в последнее время меня просто тошнит.
П.С. Для чувства риска и адреналина можно в рулетку сыграть.

Абсолютно точно, именно такой подход. Только на совещаниях надо говорить «Полный Agile с максимальным учётом пользовательского фидбека». Ну или не работать в стартапе, если тошнит. Но мы к сожалению идём туда прямым ходом, и вероятно это единственно верный подход в высококонкурентном капиталистическом мире

вероятно это единственно верный подход в высококонкурентном капиталистическом мире

Ну это вы прям загнули :) Программирование в стольких сферах применяется, что выбор огромный. И это именно выбор — никто не заставляет выбирать конкретный путь, и более того — его можно спокойно менять с одного на другой.
UFO landed and left these words here
Надежность это комплексная метрика, имеющая ненулевую цену. Всегда нужно искать узкое место. У меня есть возможность наблюдать развитие проекта, где используется больше десятка разных языков и технологий. Баги, тех.долг, легаси, проблемы с перфомансом, неоднозначности во входных данных, ошибки интерпретации требований, изменчивость ситуации, ротация инженеров и тому подобные прелести есть везде.

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

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

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

Может владеет, но:
а) их установка и настройка займёт время
б) их использование займёт время
в) (опционально) переделывание существующего кода займёт время

Для примера, возьмем тот же JS и типизацию. В самом простом варианте, мы начали разработку веб-приложения. И, да, проще создать index.html и app.js и начать разрабатывать. И, да, не просто начать разработку на TS: надо настроить среду, WebPack, инсталлировать кучу зависимостей и вспом. библ. Но для кого это не просто? Для бегиннера? Да, для него это будет не просто. А куча зависимостей и вспом. либ, не решают далее массу задач, для которых пришлось бы самому писать код? И не решают ли они задачи совместной разработки?

И вот мы проект развивается и мы имеем наш index.html и спагетти в app.js. Я согласен, что не нужно палить из пушки по воробьям, но попадать в ловушку «время/качество» тоже не стоит. Проф. за меньшее время со сложным инструментарием сделает то, что средний спец. с небольшим набором инструментов. Ну, тут уже вопрос цены. Да, можно рискнуть качеством.

С JS/TS не очень хороший пример уже со всеми этими бабелями — время настройки плюс-минус одинаково с одной стороны, а, с другой, один и тот же набор библиотек с npm, ко многим из которых типизация вообще не прикручена, или прикручена, скажем так, не до конца.

Быстрее и качественнее — это trade-off. Одно за счет другого. Или и то, и другое за счет третьего — резкого усложнения.

Классически — за счёт роста стоимости.

я вот уже давно внял совету автора и перешел на typescript, но вот сижу и думаю, почему за несколько лет работы на голом JS у меня никогда, никогда не было багов и проблем связанных с отсутствием типов, где я бы сказал «а вот если бы были типы, то все по-другому было бы»? Как так получилось-то?
А вы поработайте лет 5-10 исключительно на typescript'e, а потом перейдите обратно на javascript. Почему-то мне кажется что тогда вы такое начнёте регулярно говорить.

Во всяком случае я с javascript «близко познакомился» после того как относительно долгое время писал на java/c#. И мне в javascript компайлера и статической типизации дико нехватало. Особенно поначалу.
Это называется сила привычки — попытки решать задачи привычными средствами, а не родными для нового инструмента.
Такое естественно существует и естественно я тоже этому подвержен. Но в данном конкретном случае я бы сказал что инструмент сам по себе оказался не особо удобным. В конце-концов линтеры не без причины пользуются такой популярностью.

И я бы сказал что у меня основная проблема была в отсутствии интерфейсов. И в базирующихся на этом отсутствии проблемах с контрактами/«состыковкой» кода, разрабатываемого разными командами/фирмами.
Я бы сказал, что у вас проблема именно на стыках, а не в вашем коде. В таких случаях одно из решений — сделать границы и поставить пограничников. Например, иногда уместны микросервисы.

Отдельный микросервис для либы?

Зависит от задачи. Автор не писал, что речь именно о сторонних библиотеках. Хотя теоретически можно представить такую жуткую библиотеку, от которой нельзя избавиться (например, легаси), что ее придется оборачивать в собственный пограничный код.
Это были в том числе и библиотеки, которые писались для нас кем-то другим. И даже банально код, который в одном приложении писали разные коллеги.

Если у меня есть интерфейс, то я точно знаю что я получу. И если кто-то вдруг решит поменять контракт на «своей стороне», то он просто не сможет этого сделать.
Налажать можно и не меняя контрактов.
Какой-то странный аргумент на мой взгляд. Естественно есть много разных способов «налажать».
Но если есть возможность исключить какие-то варианты или хотя бы снизить наносимый ими ущерб, то почему бы это не сделать?
Это экономический вопрос — соотношение затрат и результатов. Вы же не будете спорить, что объявления типов — это лишние затраты?
. Вы же не будете спорить, что объявления типов — это лишние затраты?

Ещё как буду. Это не такие уж и большие затраты и они на мой взгляд однозначно не лишние.
Например потому что обычно позволяют избежать кучи других затрат. На дополнительный тестинг, на разбор чужого кода, на дополнительные проблемы при саппорте, правке багов и дальнейшем развитии/поддержке кода.

"Лишние" тут, видимо, в смысле без типов этих затрат нет, а что они позволяют избежать — это их эффективнотсь.

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

Но затраты по-любому.
Но затраты по-любому.

Угу, вот только у вас откуда-то там ещё взялось слово «лишние».

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

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

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

Но разве сам факт необязательности делает их автоматически лишними? На мой взгляд «лишние» здесь всё таки неподходящее слово.

Да, это всё лишние затраты с точки зрения получения конечного результата. Но эти затраты могут окупиться. Окупаемость некоторых из них уже принята за аксиому, но холивары по многим из них не стихают десятилетям.

UFO landed and left these words here
аксиома в том, что введение строгой типизации ничего кроме удорожания разработки не даёт.

Какое сильное и при этом ничем не обоснованное высказывание.


Вот есть, например, nalgebra, это библиотека на Rust для линейной алгебры. В ней есть тип Matrix, который параметризован, в частности, числом строк и колонок. За счёт этого, например, операция умножения там определена только для матриц с совместимыми размерами, а операция транспонирования и обращения матрицы определены только для квадратных матриц. Как вы на это будете писать тесты?

Количество тесткейсов растет экспоненциально от количества кода. В количество типов растет линейно.
UFO landed and left these words here

Учитывая аналогичную поправку для тестов, получаем, что их количество будет расти уже не как O(e^n), а как O(e^e^n), что ли?

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

Маленький вопрос: тогда почему пр-во США так любит ADA?
Оно его небезусловно любит. Только там, где нужна надёжность.

Собственно потому так смешён, нет, по настоящему смешён этот спор между тупоконечниками и остроконечниками любителями динамической и статической типизации.

Если вам нужен код, который работает гарантированно и всегда — статическая типизация ваше всё, а то может и в Ada/Idris может повезти поиграться. Собственно тот факт, что сегодня не существует ни одной операционки, ядро которой было бы написано на динамически типизированном языке, равно как нет и браузеров, написанном на динамически типизировнных языках, да и даже какой-нибудь PyPy, который, типа «интерпретатор Python, написанный на Python», если чуть-чуть поскрести, то выяснится, что нет, там нифига не Python, там restricted subset of Python that is amenable to static analysis.

А вот если вам нужно тяп-ляп и «как-нибудь, пусть оно хотя бы что-то хоть как-то на демонстрации заказчику отрисует» и тот факт, что закрытие одного бага создаёт пять новых… тут к вашим услугам и GUI и динамически типизированные языки и куча всего ещё. И тут, действительно, вам статическая типизация будет сильно мешать: потребуются более квалифицированные кадры, а скорость закрытия тасков — упадёт (новых станет тоже меньше, но если у вас всё равно почасовая оплата, то это для вас скорее минус, чем плюс).

Так что… если ваша цель — это решение некоторой задачи, которую вы можете сформулировать — то статически типизированные языки незаменими. Если вам нужен процесс (с почасовой оплатой и демонстрацией «достижений» заказчику) — динамические языки прекрасны.

Заметьте, кстати, что между этими двумя типами программ нет жёсткой границы. Например у нас почти весь код написан на на C++ плюс всякие анализаторы, и прочее… но если и генераторы кода на Python. Потому что вот там как раз важно, чтобы оно на том ровно файле, который у нас есть породило бы разумный выхлоп… и всё — больше ничего не нужно. Совсем. «Заказчики» тут мы же сами — но требования у нас такие же, как у всех: делать этот код надёжным как скала — не нужно.

Сейчас правда есть идея переписать все эти генераторы на чём-нибудь другом (рассматривается Go), но то такое: Python2 больше не поддерживается, код всё равно переписывать, а поддержка Go встроена в билд-систему Android и всё-таки лаконичнее, чем на C++.

Если бы не это — ещё 10 лет бы никто ничего не переписывал.
Вы напрасно ограничиваете процесс для динамического языка почасовой оплатой.

Бывает, например, еще поисковый процесс — когда вы не знаете, что писать, и только ищете. Нетиповая задача.

Или те же генераторы. Если сам генерируемый код меняется каждый день, нет никакого смысла писать генератор на компилируемом языке.
Или те же генераторы. Если сам генерируемый код меняется каждый день, нет никакого смысла писать генератор на компилируемом языке.

Какой-то неочевидный вывод

Вы же не будете каждый раз перекомпилировать программу при смене шаблона, если это происходит часто?

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

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

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

И заодно конвертировать все строки в числа.

Кстати, вы часто вставляете рубли в поле даты?

Со статической типизацией у меня нет такой возможности.

Вам ничто не мешает это сделать в любой момент. Часто ли статическая типизация ловила вас на том, что вы ставили рубли в поле даты?
UFO landed and left these words here
Боюсь, что одной статической проверки типов тут явно недостаточно и вы уже оказываетесь в области формальной верификации программ.
UFO landed and left these words here
Вы напрасно ограничиваете процесс для динамического языка почасовой оплатой.
Если вас интересует общее количество времени, потраченного на проект — динамические языки проигрывают (народ недаром с Python на Go переходит и даже в самом Python пытается типы как-то добавить). Если вы хотите уменьшть время, затраченное на «закрытие таски» (и вас не волнует сколько это закрытие новых породит) — выигрывает динамика.

Потому при почасовой оплате динамика выигрывает без разговоров: и денег больше получите и объяснить за что вам их нужно оплатить проще. В остальных случаях… всё весьма непросто.

Если сам генерируемый код меняется каждый день, нет никакого смысла писать генератор на компилируемом языке.
Почему нет? Если компилятор достаточно быстрый (как в том же Go) то время запуска отличается от динамических языков несущественно.

Не вся разработка ведётся в рамках проектного управления, "под ключ". Достаточно много её идёт в рамках продуктовой разработки с постоянно меняющимися требования, где для бизнеса основная метрика эффективности его команд разработки — time-to-market, время от появления бизнес-гипотезы до выкатки её в продакшен. Сами же разработчики работают на окладе и может быть и рады бы писать не то что статически типизируемый, а вообще формально верифицируемый код, но любое увеличение времени разработки воспринимается бизнесом в штыки, его нужно этому бизнесу "продавать".

любое увеличение времени разработки воспринимается бизнесом в штыки, его нужно этому бизнесу «продавать»
Было бы еще это желание.
UFO landed and left these words here

Да, кстати, хороший пример "эффективности" статической типизации для галочки. Причём сами разработчики вполне объяснимо от неё плюются.

UFO landed and left these words here
Совершенно верно. Я ж не против динамической типизации ни разу. Если вам не нужен надёжно работающий код (а это, как это ни удивительно, часто бывает так) — то вы вполне можете обходиться динамической типизацией.

Нужно просто отдавать себе в этом отчёт, а не пытаться рассказывать сказки, что вам не нужна типизация, потому что у вас тесты или потому что у вас «такая команда».

Нет, вам не нужна типизация, потому что вам не нужен код без багов. И всё. Отдайте себе в этом отчёт — и всем станет резко проще и легче.
Статическая типизация не является серебряной пулей в борьбе с багами. В программах на С++ багов полно и обычно куда более серьезных, чем рубли в поле даты, иначе бы не требовались в дополнение к компилятору разные статические чекеры и отладчики.

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

Поэтому ваше противопоставление смысла не имеет.
Имеет-имеет. Типичный web-сайт содержит десятки миллионов строк кода в ядре, SQL-сервере, массе разнообразных утилит и прочем. И небольшое количесто кода на динамических языках типа PHP или JS. Примерно на порядок, а то и на две меньшее, чем вот всё вот это вот, написанное на C/C++.

Тем не менее в 9 случаях из 10 взлом происходит через тонкую прослойку, написанную на динамических яыках, а не через кучу кода, написанного на статически типизированных языках.

Даже C и C++ (по современным меркам — ужасно ненадёжные и «опасные») содержат на два-три порядка меньше ошибок (в пересчёте на миллион строк кода), чем месиво на динамических языках, которое исполняется «сверху».
Гениальная логика.


Гениальная логика — это когда защитой от дождя считают исключительно плащ, хотя он толком и не защищает.
UFO landed and left these words here
Речь исходно шла о статических проверках типов, которые якобы решают все проблемы, а не о силе системов типов.

Оказывается, не вся статическая типизация одинаково полезна.
UFO landed and left these words here

Всё может быть проще: надёжно работающий код нужен, но не такой ценой как смена языка. Это и на уровне компании/продукта/проекта работает, и на уровне отдельного разработчика.

UFO landed and left these words here

Долго вы писали на динамических?

UFO landed and left these words here

Недолго, но это изначально эксперимент был. А вот на C и С++ я довольно долго писал под веб, пока не наткнулся на PHP и не понял насколько на нём эффективней.

Хоть есть и множество вариантов, наиболее вероятно что проект просто полностью укладывался у вас в голове. По моему мнению понимание необходимости в типизации приходит со временем и всем. Исхожу из личного опыта работы с моим коллективом (многим у нас + 20 лет в ИТ).

Как определить что нужны типы? Когда смотришь на функцию как эта и задаешь вопросы:
function (duration, divider)
Как объяснить, что надо в нее передать? А что она вернет? Чтение исходников или документация в обоих случая является недорешениями перед типами. Поскольку и то и другое позволяет передать в функцию невалидные данные, а документация ещё и имеет свойство не соответствовать действительности. В противовес типизированный вариант выглядит как
function (dration: {start: Date, end: Date}, divider: number): [{start: Date, end: Date}]

По опыту самые частые ошибки, что в JS (динамический слабый), что в C (статический слабый), что Java (статический сильный) — это соответвующие вариации NPE (вроде как из Java само понятие). А вот в PHP (динамический слабый с опциональным усилением) всё реже и реже встречаются.

Это потому, что PHP не строгий начальник, а добрая бабушка, которая не бросает NPE, а максимум тихо пожурит нотифаем и печально продолжит работать дальше, оперируя не валидными данными. ;)

При всей своей доброте эта бабушка не сможет продолжить работу при null->dosomething()

Просто когда условные js или java создавались, было не очевидно что нужно по-умолчанию запретить null и сделать его opt-in. Языки, которые созданы в последние годы (уже достаточно давно началось) обычно не имеют такой проблемы.

То есть "статическая типизация" в подобных статьях следует читать как "статическая типизация в некоторых языках, в которые не входят все(почти? Не знаю что там с сабжем в C#) мэйнстрим языках" или "я бы не начинал новый проект в 2020 на мэйнстрим языке, если он не от MS"?

Из мейнстрима с opt-in null сразу в голову приходит kotlin — выбор по-умолчанию для разработки под андроид, как я понимаю. Ещё слышал, что в rust, f#, swift, erlang нет null, но не буду утверждать. Ну а что и как следует читать в подобных статьях — понятия не имею, и сам на этих языках не пишу.
(почти? Не знаю что там с сабжем в C#)

Opt-in nullability таки завезли и можно пользоваться, правда она местами косячно работает: требует shut-up операторов, неудобно работать с nullable value types, которые появились раньше. Однако, польза всё равно заметна.

chersanya
В F# — нет null (в 99% случаев, на практике он может прилететь из того же C#). Но, к сожалению, F# и не мейнстрим язык.
Да, я не спорю что мейнстримовых языков без null пока не особо много (но они есть) — ну так ничего удивительного, что полезные фичи в них приходят медленно. Java тут, наверное, была самым ярким примером. Никто же не заставляет писать на (i) мейнстримовых (ii) статистически типизированных языках (iii) с null'ом — каждый из пунктов это личный выбор каждого. Я весьма рад, что мне про неожиданный null задумываться не приходится :)
Ну вон в С# 8 завезли «non nullable reference types». Это конечно не совсем прямо отсутствие null, но как минимум приличный шаг в этом направлении.
На C# я давно не писал особо, но это как раз хороший пример полностью мейнстримового языка, который старается достаточно быстро включать в себя полезные фичи — которые совместимы с имеющимся поведением, конечно.
Зло — это не динамическая типизация, а использование одного и того же символа для арифметических операций и конкатенации строк. В остальном с динамической типизацией всё хорошо.

Там все очень легко и при этом прямо быстро делается через [s1, s2].concat()

Если речь про js, то проблема не в том что оператор + перегружен, а в том, что он неяно приводит типы
Это называется полиморфизм.
UFO landed and left these words here
О да, единый символ для конкатенации и сложения — это единственная проблема в динамических ЯП! </sarcasm>

Большинство примеров, которые демонстрируют недостатки динамической типизации обычно относятся не к ней, а к слабой, к неявному приведению типов в частности. Даже по таким косвенным признакам как использование в примерах JS и PHP без тайп-хинтов, а не, например Python или Lisp :) можно это предположить. Я вот в частных холиварах показываю примеры PHP с type hint vs Java, когда оба падают в рантайме, но первый с TypeError, а второй с NPE и вопрошаю "в чём сила, брат? В статике или строгости? "

Очень многие путают статическую типизацию и сильную. А еще многие не понимают, что значит сильная типизация. Даже тут, в комментариях к этой статье видел заявление, что в Java сильная типизация…
Отсюда и мифы, что typescript от чего то там спасет. Но вот от кривых рук писателей библиотек он не спасает… Как и не делает ни малейшей попытки спасти от кривых рук пользователей библиотек у которых нет ts…

А какая в Java типизация? На мой взгляд, сильная статическая номинативная. Вроде как большинство с этим согласно.


От каких-то ошибок типов он всё-таки спасает, особенно с настройками построже.

Как минимум языки с сильной типизацией не имеют супертипов, вроде Object в джаве и шарпе или типов принимающих в себя что-угодно, вроде any в тайпскрипте или void* в C, ну или как минимум не позволяют свободно кастовать через такой тип что угодно к чему угодно и потом падать в рантайме если кто-то где-то вдруг ошибся.
UFO landed and left these words here

Ну не надо Java пинать, у неё-то как раз система типов довольно кривая.

UFO landed and left these words here
2. разделяем числовое и строковое сравнение

И теперь невозможно написать обобщённый код, опирающийся на операции сравнения.

А сейчас возможно? + для конкатенации строк, но — / * для строк нету. Покажите мне тот обобщённый код на JS, который сможет воспользоваться этим с пользой.
Сумма в массиве и в массиве строк? Но это 1 частный случай, и ради него создавать проблемы в куче других мест глупо.

Я писал про сравнения, а не про арифметические операции.

UFO landed and left these words here
1. с чего вдруг?

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


2. сравнения строк и чисел — разные по своей природе. и именно попадание строки вместо числа и числа вместо строки вызывает максимум проблем в языках где недоделана динамическая типизация (например python, javascript)

В Perl, получается, тоже недоделана:


my $var = "13";
if ($var == 13) {
    print "equal";
}

Выводит equal.

UFO landed and left these words here
И теперь невозможно написать обобщённый код, опирающийся на операции сравнения.

все намного хуже. Операция сравнения для символов в общем виде не определена. Не надо только про юникод. IJ — это то же самое, что ij? Или вовсе IJ? Честно — сравнение строк нужно, чтобы их алфавитно упорядочивать. А это в разных языках (локалях) разные правила.

Операция сравнения для символов в общем виде не определена. IJ — это то же самое, что ij?

По такой логике и сравнение между числами не определено. Возьмём повсеместные для floating point +0 vs -1, NaN vs NaN и другие. Да даже формально определённые -1 vs +1, 10.5 vs 10 — иногда их полезно считать одинаковыми, так же как вы пишете про ij vs IJ.
Возьмём повсеместные для floating point +0 vs -1, NaN vs NaN и другие

вся работа с FP производится согласно стандарту. Т.е. ученые мужи сели и договорились. Как оно должно быть. А потом уже весь софт и даже оборудование работает по этим принципам. Но вообще — да, иногда логика работу с FP бывает… загадочной.
Со строками же проблема вроде как до сих пор не решена (?).

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

Оно нерешаемое, конечно, но сложно решаемое.

UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
крайне маловероятно что неудачный merge приведёт к замене килограммов на доллары.
Как было бы хорошо, если это действительно так.
UFO landed and left these words here
крайне маловероятно что неудачный merge приведёт к замене килограммов на доллары

Легко, достаточно смело поменять порядок аргументов функции, в другой ветке сделать новый вызов функции, который соответственно не исправлен при смене.

Я регулярно влетаю в boolean blindness и аналоги для других примитивных типов. И это при живом-то newtype! Сначала лень, а потом приходится исправлять конечно.


А уж сколько багов NonEmpty отловил!

UFO landed and left these words here
Зачем вообще в данном случае конкатенация если есть всякие join или интерполяции строк?
UFO landed and left these words here
Если говорить о конкретных багах несовместимости типов, то сложение строки с числом и вправду редко вызывает проблемы. Чаще проблема в неразрывной связанности кода с мозгом программиста. Попробую обобщить на очень жизненном примере.
def call_bar(bar):
  bar.call()
class Foo:
  bar = None
  def op(self, bar):
    self.bar = bar
  def call_bar(self):
    call_bar(self.bar)
foo = Foo()
foo.send_bar() # упали
foo.op(object)
foo.send_bar() # не упали

Типичная ошибка на самом деле по опыту использования софта на ряде нестрогих ЯП.
В чем тут проблема: контекст, когда вызов foo.call_bar() валиден, а когда невалиден, находится в голове программиста. Неявная сцепленность с контекстом, о котором должен знать программист. Юнит-тесты это не ловят, увы.
Неявная (она существует, но только логически, в голове у программиста) сигнатура call_bar принимает только Bar. А мы ей передаём сумму None|Bar.
Если писать всё логически корректно (без неявной зависимости совместимости от контекста), то получается строго типизированный код, но без меток типов. Проставить эти метки — дело небольшого времени, если речь не о трех строчках кода на выброс.
Еще с явными типами намного легче ловить ошибки при изменении контрактов (сигнатур). Это вторая или первая по распространенности причина багов. Юнит-тесты далеко не всегда покрывают полностью интеграцию кода.
Всё правда. Но от того что вы это написали — ничего не изменится.
Ну ещё напишу, какие проблемы. Ещё более зло. А потом ещё раз.
Предлагаю вам для вдохновения песню Queen «Death on two legs». Она посвящена какому-то менеджеру, который их обижал, и чуть менее, чем полностью состоит из изощренных ругательств.
Ого, а я думал, что эта тема давно закрыта. Оказываются, что есть люди, читающие динамическую типизацию подходящей для некоторого класса задач. А ведь она подходит только для небольшого объема кода, который не будет дальше развиваться.
UFO landed and left these words here
> все сложные алгоритмы лучше делать на языках с динамической типизацией.

Чтобы разгребать не только логические ошибки, но и ошибки несоответствия типов?
UFO landed and left these words here
Вы хотя-бы обобщенную сортировку по compare функции сможете написать, не отвлекаясь на то, что кто-то обязательно пихнет туда два объекта, которые не будут Comparable между собой?

И это ведь не специфичный кейс. Обобщенные алгоритмы частенько требуют выполнения специфических условий объектом поданным на вход. Да простейший бинпоиск требует сортированных данных.
Будете писать в начале функции
if(notSatifyCondition(array)){
throw Exception()
}?

Может лучше все же объявить сигнатуру как search(arr: SortedArray) и обойтись без if и рантайм ошибок? Сейчас действительно 20й год 21 века, а вы такие проблемы предлагаете решать не автоматически за счет компилятора, а вручную подпихивая костыли.
UFO landed and left these words here
вопрос какой оператор сравнения вы хотите

Тот, который предоставляет пользователь, когда вызывает сортировку.

UFO landed and left these words here
А что выбор то такой маленький? Хочу сравнивать любые множества, задавая отношение порядка произвольным образом.
Вот есть у меня Enum, и мне очень захотелось по нему что-то упорядочить. А еще есть объект содержащий Response от сервера — и я хочу упорядочить ято-то другое по информации из этого Response. И при этом хочу запретить пользователю сравнивать Response и Enum — это же бред, как сравнивать слона и шкаф. Ваши динамически типизированные действия?
UFO landed and left these words here

В бизнес-требованиях может быть, что enum должени сортироваться и не по символам, и не по числам, например OPEN должен быть на первом месте, CLOSE на втором, и т. п. по важности для пользователя. Причём в разных юзкейсах разная важность.

Окей.

Вот вам задача. Практическая, не какое-то теоретическое занудство.

Пишем менеджер задач. Там есть процессы. Активные, на паузе, завершенные и не начатые.

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

Можете хоть к числам приводить, хоть к строкам, хоть вообще ENUM не пользовать. Накидаете решение? Я в ответ на него скину свое.
Как говорится, болтовня ничего не стоит — покажите мне код)

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


Тут типы только навредят. Функция sort/3 должна принимать два любых элемента и функцию двух аргументов. Если эта функция вернула не boolean — отказ (это бизнес-логика, тут каждый сам решает, я бы в лог ворнинг бросил и обработал, как если бы вернулось true), если true — первый выше второго, если false — наоборот. И все.


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


Единственная проверка типа, которая тут имеет смысл — это проверка возвращаемого сортером значения на boolean. Но вокруг хайп, и куча неглупых людей как заговоренные повторяют нелепую мантру про «типы помогают при рефакторинге» — поэтому горе-разработчики тут нагородят типов и обломятся ровно на следующем нестандартном требовании к сортировке.

Так, может быть, проблема не в статической типизации (раз она тут, пусть и по мелочи, но всё же поможет), а в этом самом "хайпе"?

Не очень понял вопрос; я никогда и нигде не заявлял, что со статической типизацией есть проблемы. В некоторых случаях — она вполне уместна.


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

Функция sort/3 должна принимать два любых элемента и функцию двух аргументов.

А вот это смешно. Потому что у вас тут есть тип. Я его в хаскель формате не напишу — слабо. Но очевидно, что сигнатура функции высшего порядка (которой является sort/3) вполне конкретно определяется. Как и сигнатура композируемой функции.


это проверка возвращаемого сортером значения на boolean.

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

Вам это смешно, потому что вы не понимаете, что такое тип.


На Хаскеле такое не написать, потому что там нужен завтип. На Идрисе можно. А у меня там просто паттерн-матчинг в динамически типизованном эрланге.


эта проверка должна быть на этапе компиляции

Конечно. А при чем тут типы, стесняюсь спросить?

UFO landed and left these words here

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


сделайте, пожалуйста, ровно n запросов в базу перед вызовом сортера

Да сделаю я, сделаю. Я один запрос сделаю, а не n даже, которые вам подарила волшебная система типов (но не уберегла от говнокода, правда, но это случайно, я уверен). Пример не очень удачный, но ведь пуговицы — это единственное, к чему тут можно прицепиться.


На каждый вызов сортера?

При чем тут вызовы вообще? Я указал, где тип важен, нужен, интересен.

UFO landed and left these words here