Открыть список
Как стать автором
Обновить

Комментарии 409

Да, хорошая саркастическая статья. Оценил юмор

Оригинал опубликован 1 апреля. Статья соответствует )

Немного толстовато, но в целом норм. Про пару моментом не знал, например.

Не ловкая ситуация, когда читаешь и принимаешь за чистую монету.
Юмор уровня школьников. У нас помнится класс поделился на сишников и паскалистов, и чуть ли не до драк доходило. Там подобные писюльки были в ходу.
… и так будет вечно…
Странно, вроде не пятница, а «смИшная статья про JS» есть.

Ну раз завтра выходной, значит сегодня — пятница.

Надо было до завтра дотянуть. Первоапрельская статья, первомайская… Какая разница?! )

День победы! Javascript над Golang

С 9м мая не путаете?

Хорошо что это всего лишь юмор и человек останется гофером. Нам тут клоунов не нужно...

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


Вот не в моих правилах вообще опускаться на личности и так бомбить, но откуда столько людей с синдромом графомании?


Как им времени своего не жалко только эти статейки калякать.


// я понимаю, что это перевод

Вопрос не "почему" оно так работает, а "какого хрена".

Ну если человек искренне считает, что вот эта конструкция в чем-то неправильна:


(1,2,3,4,5,6) === (2,4,6)

То надо не статьи писать бежать, а читать доки про операторы скобок и запятой. И то, что они возвращают последнее выражение.


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


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

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

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

В приличных спецификациях на каждое решение есть убедительная аргументация: это решает проблему неоднозначности, это необходимо для оптимизации, это улучшает продуктивность. В джиесе такого нет. Решения, принятые спонтанно по зову сердца, просто оказались с годами высечены в камне. Динамическая типизация? Ну, двадцать пять лет назад это было модно, особенно для крошечного скриптового языка, каким планировался джиес. Убогое стандартное API даты таково, потому что его взяли под копирку из Java. Даже название (!) украдено у Java, чтобы словить хайп того времени.
Ох уж этот юношеский максимализм… Объем памяти в голове сильно ограничен. Некоторые языки позволяют запомнить несколько универсальных правил взаимодействия сущностей языка, и на основе них дальше можно писать любые программы. А другие языки кишат исключениями из этих правил, за которыми постоянно приходится лезть в спеку и тратить на это время.

Возможно вам близок такой подход, мне нет. У технаря должно быть строгое понимание того, что и как он делает. Математика, инжниринг и программирование это не худ. литеретура, где достаточно вдохновится одной книгой и написать похоже другую. Если вам лень или если конкретно у вас ограничен объем того, что можете запомнить — ну это тогда не мои проблемы и не проблемы остальных людей, коих не мало и кто программирует на JS.


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


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

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

Ну так а что хорошего во флексибилити? Для реализации чего-либо в языке необходимо и достаточно иметь всего один простой способ. Если в языке есть много способов, причем ряд из них приводят к трудно обнаруживаемым ошибкам, это плохой язык. Кидайте в меня любыми какашками, но JS, это объективно очень плохой язык, которому круто повезло в своё время оказаться единственным скриптовым языком для очень перспективной платформы.
JS, это объективно очень плохой язык

Любая объективность должна иметь объективные метрики. В студию, пожалуйста.

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

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

Увы в промышленном программировании (и не только программировании) нельзя просто так взять и отказаться от чего-то, даже когда ситуация круто поменялась и появились лучшие альтернативы. Обратная совместимость очень важна. Есть конечно смельчаки «выкинуть всё старое и переписать с нуля», но они как правило плохо кончают)
В JS иначе — много флексибилити, много специфик.

много специфик не из за того что динамическая типизация и много флексибилити, а просто так случайно получилось, язык «разработали» в спешке, так и оставили ради обратной совместимости. В том же пайтоне при динамической но более строгой типизации, WAT моментов куда меньше.
У технаря должно быть строгое понимание того, что и как он делает.
Полностью согласен. Проблема в том, что джиес заставляет не понимать, а зазубривать. Инженерный подход же предполагает поиск оптимального, элегантного решения, а не закидывание задачи костылями и копипастой.
Если вам лень или если конкретно у вас ограничен объем того, что можете запомнить — ну это тогда не мои проблемы и не проблемы остальных людей, коих не мало и кто программирует на JS.
Если вы гордитесь тем, как мастерски умеете есть кактус и почти не колетесь — ваше право. До относительно недавнего времени это было неизбежно, т.к. джиес занимал уникальное положение и не имел альтернатив. Но сейчас им достаточно наелись, что появились Typescript, WebAssembly, ReasonML и наконец-то ситуация стала улучшаться.
автор просто хотел выплеснуть свою желчь
С этим тоже согласен. Но «желчь» подана тонко и вполне оправдана.
Я понимаю, о чем вы говорите. Но я искренне не понимаю, зачем писать такой код в JS. Может, человек ожидал наличие кортежей (сарказм), может еще что-то, конечно, но все же. Ни в одном языке не нужно использовать конструкции, с которыми ты не знаком. И о типах, да, я согласен, что здешняя система типов местами отвратительна, но ее действительно нужно знать. В C++ типизация тоже не на высоте, но никто в здравом уме не будет отрицать, что этот язык очень важен во многих сферах, и нужно знать о некоторых нюансах его реализации, чтобы его использовать.
Но как рофл — статья действительно хороша. xd
P.S. Как поживает ваш компилятор LENS? Будут ли новые статьи на тему компиляции?
C++ никто и не считает образцом изящного дизайна ЯП, скорее наоборот.

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


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

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

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

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


Но не язык. Конечно проще обвинить JS, а не что-то другое. Но от этого он хуже не станет. Хейтерс гонна хейт )

Понимаю ваше разочарование в такой ситуации — но, тем не менее, язык не виноват.

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

Те языки, которые вообще не требуют усилий для написания адекватного кода очень ограничены. Вы сами сказали, что говнокод можно писать на чем угодно, но почему вы уверены, что для написания нормального кода на js (es6, TS) требуется усилий больше, чем на любом другом? К тому же везде используются линтеры, которые предотвращают написание плохого кода. Я не знаю что у вас происходит на работе, но такое чувство (по вашим комментариям), что компании вообще пофигу кого нанимать, "лишь бы работоло", а как работает — наплевать.

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

Что разумеется неверно. Или всегда писать === вместо == для Вас усилие? Ну да, печатать дольше. И да, это один из минусов JS, т. к. == можно было бы выкинуть, а === сделать на 1 символ короче. Но не такой, что всё, катастрофа. Я написал ниже хороший комментарий с пояснением всех пунктов из этой статьи.

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

Давайте засунем это в сишарп и посмотрим что он скажет:


var x = (1,2,3,4,5,6) == (2,4,6);

CS8384 Tuple types used as operands of an == or != operator must have matching cardinalities. But this operator has tuple types of cardinality 6 on the left and 3 on the right.

Как по мне, это куда более логичный результат чем false, и особенно true.

или в питон

>>> (1,2,3,4,5,6) == (2,4,6)
False
>>> (2,4,6) == (2,4,6)
True
Или в Delphi:
  var x := [1,2,3,4,5,6] = [2,4,6];
  // FALSE
  var y := [2,4,6] = [2,4,6];
  // TRUE

Но если
  var x1 := [1,2,3,4,5,6];
  var x2 := [2,4,6];
  var x3 := x1 = x2; // E2008 Incompatible types
В JavaScript для квадратных скобок тоже всё «нормально»:
[1,2,3,4,5,6] == [2,4,6]
// false
[2,4,6] == [2,4,6]
// false
Упс

Что значит упс? У Вас два разных объекта, с чего вдруг они должны быть равны?


Если бы это было не так, у меня были бы претензии к языку. Это было бы достаточно странно, что равны разные объекты.


А оператор "," в JS — очень крутая вещь, не раз меня выручал. То, что Вы не знаете юз-кейсы, не значит, что их нет.

У вас случайно нет претензий к JS, что "foo"=="foo"? Объекты-то разные.
Объекты-то разные.

Эм, кто Вам сказал? В данном случае это скорее всего будет один объект внутри движка (по крайней мере в V8).


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


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


В итоге "aaa" и "aaa" — это абсолютно одинаковые объекты с точки зрения программиста, поэтому === говорит, что они одинаковые. А вот {a: 1} и {a: 1} — это два разных объекта с точки зрения программиста.

Эм, кто Вам сказал? В данном случае это скорее всего будет один объект внутри движка (по крайней мере в V8).

Не вопрос, давайте сделаем так


"foo1" === ("foo" + 1)

В итоге "aaa" и "aaa" — это абсолютно одинаковые объекты с точки зрения программиста, поэтому === говорит, что они одинаковые. А вот {a: 1} и {a: 1} — это два разных объекта с точки зрения программиста.

А этого вы не можете знать. Например, с моей точки зрения вполне вероятно что {id: 10} и {id: 10} — это один объект.

А этого вы не можете знать. Например, с моей точки зрения вполне вероятно что {id: 10} и {id: 10} — это один объект.

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


Это как с близнецами — если они выглядят одинаково (например, на фото), это не значит, что это один и тот же человек. Чтобы это проверить, нужно использовать оператор ===.


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


При этом со строками такой проблемы нет, т. к. изменить строку в JS невозможно (можно изменить ссылку в переменной, но содержимое самой строки). Поэтому даже если строки на самом деле разные, мы можем считать их одинаковыми — это никогда не приведёт к ошибкам.


Причём если бы это было не так, то и со строками в JS было бы тоже самое, что с обычными объектами: "aaa" !== "aaa".

У меня по бизнесу часто бывает нужно трактовать такие объекты как одинаковые (базовый пример — когда я иду делать апдейт в базу а у меня есть два объекта с одинаковой айдишкой и разным value). Так что


В итоге "aaa" и "aaa" — это абсолютно одинаковые объекты с точки зрения программиста, поэтому === говорит, что они одинаковые. А вот {a: 1} и {a: 1} — это два разных объекта с точки зрения программиста.

Вот я — программист, и у меня есть необходимость как различать "aaa" и "aaa", так и считать одинаковыми {a: 1} и {a: 1}

У меня по бизнесу часто бывает нужно трактовать такие объекты как одинаковые

Для этого есть deepEqual.


Строка (также как и обычные числа) считаются простым неизменяемым значением, поэтому сравниваются по содержимому, а объект сложным изменяемым, поэтому сравнивается по ссылке (причины объяснены выше — всё дело в изменяемости).


Если нужно сравнить по содержимому, юзают deepEqual или equal, а не ===.


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

часто бывает нужно трактовать такие объекты как одинаковые

Дайте универсальное определение "одинаковости"

x = y, если для любого предиката P: P(x) <=> P(y).


Дальше можно вводить понятие отношения эквивалентности и рассматривать фактор-множество/группу/кольцо/вотэва по этому отношению (с, вероятно, усиленной структурой).

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


2 разных объекта совсем не ведут себя как одинаковые сущности. Мы начинаем изменять объект, и если мы считали их одинаковыми, будет фейл. А, к примеру, строки и числа ведут себя как одинаковые сущности — что бы программист не делал, невозможно написать такой код, чтобы можно было отличить их, либо чтобы их различие/одинаковость привела к ошибкам в коде. Причина этого проста: числа и строки нельзя изменять. И да, это касается не только строк, но и чисел: в памяти две "5" (пятёрки) не значат, что это два одинаковых числа, в реальности скорее всего они будут продублированы, т. е. это будут две разные "5". Но программист никак не может это узнать и написать код, который бы привёл к ошибке из-за считания их одинаковыми. Всё потому, что "5" никогда не изменяется, а изменяется только значение переменной. Сама "5" так и остаётся "5" навечно.


Как итог: числа и строки считаются одинаковыми сущностями, объекты нет.


Более того: если в каких-то языках сделано не так, у меня будут очень серьёзные претензии к этим языкам.

Но ведь...


> let P = (x) => x === 5;
undefined
> P(5)
true
> P("5")
false

Не очень понял, что тут не так. P выдало разные результаты, и при этом в JS 5 !== "5". Всё работает как надо.

Но вы же сами писали:


числа и строки считаются одинаковыми сущностями, объекты нет.

А они не одинаковые.

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

Разумеется, имелось ввиду, что, к примеру, число 5 и 5 — это в JS одинаковые сущности, как и строки "5" и "5". Выполняются равенства (5 === 5) и ("5" === "5") (т. е. они одинаковые между собой).


Конечно есть фишки с даблами типа 0.1 + 0.2 !== 0.3, но это потому что сам процессор при сложении 0.1 и 0.2 получает другое число (не 0.3) из-за потерь точности. Это во всех языках так.


Это достаточно логично, что, к примеру, 5 и 5 равны между собой, даже несмотря на то, что в памяти это совсем разные пятёрки. Почему так и должно быть, я объяснил выше. JS максимально логичен здесь. А вот другие языки не всегда, как я догадываюсь. Если строки могут изменяться, то ок, тогда правильно (но тогда бы неплохо ввести неизменяемые строки). Но если не изменяются, и их нельзя сравнить с помощью ===, с языком явно что-то не так. Это тоже самое, если бы 5 было бы не равно 5, а для сравнения нужно было бы писать 5.isEqual(5). Смысла в этом ноль, т. к. не существует случая, когда сравнение 5 === 5 подвело бы программиста (почему — написал выше). Аналогично и со строками.

С точки зрения JS (да и некоторых других языков) == проверяет именно на одинаковость, на одинаоковое значение, а === на идентичность, на тожесамость.


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

так себе пример, поскольку в js массивы это объекты, а объекты так сравнивать нельзя.
Потому что так решили разработчики получилось. Обьектно-ориентированный язык — а обьекты сравнивать надо каким-то хитрым способом…
1. Он не объектно-ориентирован, а прототипно-ориентирован. Всё ООП в js — то натяжка совы на глобус (ради удобства программистов ООЯП).

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

Как минимум, прототипно-ориентирован — это и подразумевает ООП.


А во-вторых, как его там ориентировали не знаю, но язык имеет очень красивое ООП.

В современных Дельфях стало можно объявлять переменные, не указывая тип?

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


А вообще в js количество ВТФ после определённого предела сильно падает. Из последнего, что вызывало втф — работа с датами, точность при делении и, цук, неочевидное наличие this в static методах.


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

Когда после js берешь python, там тоже хватает wtf моментов и ощущение странного, кривоватого дизайна тебе долго не покидает.
Думаешь, ну емае, в js было все попроще и логичнее. :)
Было бы интересно увидеть примеры.
Ну например, отсутствие switch огорчает тех, кто к нему прикипел душой.
Неявный return например. Не очень понятные импорты. Странная работа с файлами, похожая на with из js, отсуствие switch, да. Неуклюжие пляски с virtualenv.

Нельзя сказать что эти мелочи прям ужасные, но таки кажутся раздражающей архаикой. В процессе работы все это сходит на нет, привыкаешь ко всему, тем не менее не могу сказать, что python прям стройнее и удобнее js.
В примере Delphi в 1-м случае — множества целых чисел, а во 2-м — векторы целых чисел (внезапно).
в python, как и в шарпе — кортежи

Не хочу влазить в холивар, но в js tuples вообще нет и конструкция все таки достаточно однозначная — скобки работают как группировка параметров и их можно опустить, а значит это эквивалентно
var x = 1,2,3,4,5,6 == 2,4,6;


Зная что означает операнд «,», никаких вопросов не должно быть. Разве что операнд странный, но он не только в js есть, а в том же С. И если там парсер съест скобки (почему бы и нет, это опять же как группировка), результат будет тем же. Да даже в C# до появления tuple результат такой же. Странно ожидать поведения как в каком-нибудь го от конструкции, которой вообще нет в ja.

PS, не совсем эквивалентная, потому что он сравнивает результаты раскрытия скобок, и будет 6 в каждой из них. В моём «эквиваленте» он сперва сравнит 6 и 2, а потом отбросит этот (как и остальные результаты), и вернёт 6 как результат всей конструкции, но это допущение ради наглядности

На самом деле не эквивалентно, потому что в декларации var запятая разделяет объявляемые переменные, и в данном случае будет ошибка парсинга:
var x = 1,2,3,4,5,6 == 2,4,6;
// Uncaught SyntaxError: Unexpected number

Потому что в C# это Tuple, в Питон это массив, а в JS это comma-оператор. Очевидно ведь.


Вот как вы звучите:


5 % 3 = 2. А!!! ЧТО ЗА БРЕД 3 — это не 2 % от 5.

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


Ну вот к примеру рубист придёт и напишет:


function f(x,y) {
    x*x + y*y;
}

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


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


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

Потому что в C# это Tuple, в Питон это массив, а в JS это comma-оператор. Очевидно ведь.

В Python это не массив, а тоже tuple.
В Python вообще не используют термин «массив»: вместо массивов там списки.

Это проблема ваших ожиданий, а не языка.

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

"Отвратительный и непонятный язык! Как таким вообще можно пользоваться? Нужно срочно писать ироничную статью!

Я не знаю питон, но судя по комментариям здесь, питон — жуткая хрень, ибо имеет овердофига серьёзных недостатков. При этом JS практически не имеет серьёзных недостатков (подробнее см. здесь), но имеет мелкие недостатки, которые практически не мешают работе. Также JS имеет огромную кучу настоящих достоинств, есть ли столько достоинств в Питоне, не знаю.

а какие-то по принципу «потому что я так решил»

Явно не в данном случае, т. к. использовать запятую как оператор — это очень логично, т. е. языки C, C++, Javascript, Perl достаточно логичны здесь.


какие-то разрабатываются по «принципу наименьшего удивления»

Ну вообще синтаксис JS во многом взят из C и C++, и как раз наименьшее удивление, думаю, будет, когда запятая будет работать как оператор. Т. е. это не только логично, но и более привычно.


Конечно можно привести аргументы и за другое поведение запятой, но это уже на вкус и цвет… Хороший аргумент — возможность быстрого сравнения кортежей сразу из нескольких значений. Но теоретически в будущем может появиться что-то типа такого: {{5, 6, 7}} === {{6, 7, 8}}

С, C++ и особенно Perl уж никак нельзя считать образцами грамотного синтаксиса.

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

Нет, если язык не оправдывает ожиданий, это может быть и проблема языка. А то так-то и с С++ нет проблем, просто у вас неоправданные ожидания что x + 1 при переполнении может быть меньше чем x.


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


а в JS это comma-оператор. Очевидно ведь

Не очевидно

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

Какие языки в 1995-м году записывали так таплы?

То JS обязательно должен был сделать этот кусок так же как в одном другом языке, который на тот момент был малопопулярен. Правильно?

Не должен был.
Но неудивительно, что в 2020 году языки, тянущие «тяжёлое наследие проклятых 90-х», выглядят менее привлекательно, чем созданные позднее и научившиеся на чужих ошибках.

ML и его потомки, Haskell.
Другой вопрос – какие языки, кроме C и C++, записывают так «вернуть правый операнд»?

классическое «не баг, а фича». Наличие документации к кривому решению не придает ему элегантности

Посмотрел, что это вроде бы как первоапрельская шутка. Ах шутка. Шутка же, ну — че ты?


Так вот, есть одно видео — вроде тоже "шутка" — https://www.destroyallsoftware.com/talks/wat
Тоже про JS в том числе. И я сам знаю массу людей, которые посмотрев такое видео совсем не будут думать так:


Ах это же незатейливая шутка, пойду-ка почитаю спеку и начну понимать!

А будут думать вот так:


Ох е*ать, да я такое гавно даже трогать не буду.

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

Может потому что гоу логичней Js. Да в гоу не хватает некоторых вещей, но там хотя бы с логичностью все нормально.

В го тоже есть не однозначные вещи. Изменится ли поле ban после выполнения цикла?


type Person struct {
    name string
    ban bool
}

func main() {
    persons := []Person{Person{name: "Alex", ban: false}}
    for _, person := range persons {
        person.ban = true
    }
}

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

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

Не может. Гоу — отвратительный и крайне нелогичный язык. Хуже всего, что он притворяется статическим, а на самом деле таким не является. А ещё у него дизайн намного сырее, чем в ЖС.

Хотя мне нравится больше js (es6, ts), но я не могу согласиться с тем, что го отвратительный язык. Сами разработчика языка говорили для какой группы людей он предназначен. Если мы возьмем языки с низким порогом вхождения (Js, PHP, Python, Golang), то я бы больше рекомендовал Go новичкам, т.к. именно он накладывает больше ограничений на стиль написания, те же типы. Т.е. для меня сейчас Go это как паскаль раньше.
Насчет дизайна я не понимаю что именно вы имеете ввиду.
именно он накладывает больше ограничений на стиль написания, те же типы

Он накладывает ограничения слишком слабо. Из статических языков с низким порогом вхождения хороши C# и TypeScript. А Гоу — отвратительный язык, который прививает отвратительные практики. У него WTF не на уровне "вот метод странно называется", а на уровне "какой идиот так сделал архитектуру языка?"


Вот пример, первый комментарий: https://habr.com/ru/company/mailru/blog/341822/#comment_10510426

Я бы не рекомендовал новичку TS просто потому что в TS есть any. Даже если взять юзкейс коллбэка. Вряд ли новичок будет писать тип функции коллбэка…
Насчёт шарпов — я бы не сказал, что шарпы на равне с Го по лёгкости изучения (хотя шарпы был вторым языком после плюсов, которые я изучал в 2014).
Я согласен с вами, что Го это не самый лучший язык для решения определённых задач, но я писал именно про то, что для новичка он бы подошёл и сравнил его с паскалем (лично нас обучали Паскалю в школе).
А вот дальше, когда человек понимает прелесть ООПа, понимает смысл документировать свой код, абстрагировать модули, тогда да, Го с этим справляется не очень хорошо (если брать пункт про ооп, то не справляется вообще)
А про ограничения я имел ввиду то, что го накладывает ограничения на стиль написания кода (у него есть офф форматер и компилятор ругается на неправильный стиль)
Насчет прививает отвратительные практики — новички не будут писать настолько сложные программы, чтобы вышли за пределы общих практик

Я бы не рекомендовал новичку TS просто потому что в TS есть any

Который стараются не использовать в реальном коде в отличие от того же Go.
А в Go any — это просто идеология и часто — единственный путь. Я про interface{} если что.

Я помню interface{} в го, я помню боль xD. Это из личного опыта, а не стёб, если что =)
Вы правы что any стараются не использовать, но я всё же говорил про новичков в программировании. Лично я помню свои первые ощущения от разработки ПО (О, это работает, ура! Нужно добавить больше функционала), а при таком настрое наврядли люди задумываются про документированность (да, я считаю, что указание типов относится к документированию).
Наврятли новички зайдут настолько глубоко, чтобы у них было многое завязано на interface{} (вспоминая свою первую учебную программу, где я просто реализовал туду, там с interface{} я сталкивался только при сериализации данных)

Просто странно, что в TS для вас присутствие any — аргумент, а в GO — нет.


Я бы не рекомендовал новичку TS просто потому что в TS есть any

Хотя в TS в отличии от Go можно вообще без any, а в Go рано или поздно приходишь к тому, что выразительности не хватает

Дап сам не знаю на 100% почему в Го это не такой сильный аргумент как в TS для меня. Возможно, в Го ты не так быстро столкнешься с interface{}, как в TS. Возможно при просмотре кода других людей я чаще встречал any, чем interface{}
Насчет выразительности я согласен, но боюсь, что нас другие могут не понять, почему мы го обвиняем в этом =)

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

Я не говорю про всех, возможно это только в моей "шараге" студенты толком не разбираются со структурами. Но по правде, у нас до 4 курса доживают 5 из 30 студентов.
Под новичками я всё же имел ввиду именно начинающих погружаться в мир программирования. Даже сравнил Го с паскалем.
Я не спорю с тем, что на Го неудобно решать какие-либо задачи. Но вы согласитесь с тем, что на Го удобно запустить сервер из коробки?

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

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

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


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


Я отлично помню как я окунулся в программирование. В конце нулевых я пошел на курсы в институт, чтобы подготовиться к ЕГЭ. Помимо всего прочего решил походить на информатику, там доплата была типа 1000 рублей в месяц. Прихожу, нас сажают за стол, на доске объясняют всякое, часа через пол говорят садиться за компьютеры и написать программу: определить, оканчивается ли число на цифру. Достаточно быстро сделал, мы показали наши результаты, урок закончился, я пришел домой. И стало мне интересно: как проверить, заканчивается ли число на другое число? В итоге просидел несколько часов, так крутил, эдак, в итоге родил нечто с двумя циклами и пятком goto, которое как-то в франкенштейновском стиле работало. Тут и вопросы пришлось решать, а что если число вообще меньше чем делимое (для цифр такого кейса нет), и вообще как это сделать думать..


В общем, мне кажется именно вопрос "а как вот так сделать" и определило мою профессию.

Ну блин, такой интересный рассказ, чем-то похож на мою историю (тоже сайтики, дкп система для клана и т.п.), а в итоге вы в последнем абзаце написали "а как вот так сделать", что больше относится к написанию нового (по-моему мнению), а не "а как это работает", что относилось бы к изучению =)
Всё же я считаю пример с бензопилой не совсем уместным (там травмоопасно =) ). Да и вы сказали, что обучать нужно с азов, а что если некому обучать?) Что если у вас есть желание творить, вы хотите создать тот же сайт, как быстро в вас погаснет искра, если начинаете изучение со структур?)
Лично я начал изучать структуры в школе, на оллимпиадном программировании, но к тому моменту я уже разрабатывал другие программы несколько лет =)
Ладно) мне кажется невозможно дать объективный ответ.
Кстати там пониже мы обсуждаем как раз поверхностные знания и к чему это приводит xD

А будут думать вот так:

Ох е*ать, да я такое гавно даже трогать не буду.


Мне кажется у языка тут больше заслуга чем у wat видео.
Ну да, прочитает кто-то такую статью и решит не становиться js-программистом, в мире появится на полтора псевдо-десктоп приложения меньше и где-то будет остывать незанятый транскомпиляцией процессор. Ужасная картина, прям антиутопия.
Человек, который не может переплюнуть, старается оплевать.
От создателей «Брат за брата — за основу взято.» и «Он обидел, слёз не видел.» Как Вы подтвердите истинность вашего утверждения?
Он просто за цитаты и двор стреляет в упор.
Ок, давайте так, есть правила написания хорошего кода, JS не соответствует им. Если я напишу в проекте метод сортировки, который будет сначала приводить элементы массива к строке, а потом сортирует и назову его не «sortAsString», а просто «sort» — меня по головке не погладят.
В частности поэтому JS это кал говна, а не язык. Я не могу помнить всю документацию наизусть и знать что там себе нафантазировали те кто писал реализации.

Я надеюсь вы понимаете то вы спорите об default-ом значении аргумента компаратора? Т.е. лексикографически сортировка производится ТОЛЬКО тогда когда вы сами так захотели. В противном случае вы просто указываете нужный компаратор и всё работает как вам надо. Причина почему .sort сам не угадывает какой компаратор вам нужен кроется в динамической природе типизации. У вас в нём могут быть как строки, так и числа, и объекты и пр…


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

$ python
Python 2.7.16 (default, Mar 20 2019, 12:15:19)
>>> n = [-2, -7, 0.0000001, 0.0000000006, 6, 10]
>>> n.sort()
>>> print n
[-7, -2, 6e-10, 1e-07, 6, 10]

??

и что вы хотели этим показать?


m = [-2, "-1", "AAA", -10, 10, 100, "BBB", "ZZZ", "1000"]
m.sort()
print m
[-10, -2, 10, 100, '-1', '1000', 'AAA', 'BBB', 'ZZZ']

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


Но не зная этого факта наверняка я бы не решился использовать этот .sort() как есть. Причём ни в python, ни в javascript. Нельзя писать такой код вслепую на удачу. А это приводит нас к тому, с чего мы начали — это всего лишь спор о дефолтном компараторе в методе сортировки массива.

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

Я честно говоря с трудом могу себе представить зачем мне может потребоваться отсортировать массив выше так, как это сделал python. Какую задачу это решает? Правда я с трудом понимаю зачем мне в целом такой массив может потребоваться. Из реальных примеров это когда у вас массив чисел состоит по факту из чисел и строко-чисел (такие грешки в JS мире встречаются часто). Но тогда обе сортировки дадут убогий результат. Так что пожалуй соглашусь, что default comparator в python выбран несколько лучше. Но я решительно не вижу никаких "WAT?" в лексикографическом варианте. Я нахожу это очень даже логичным и последовательным поведением. Просто в python решили сделать более продвинутую версию. Молодцы.

Второй питон уже даже официально не поддерживается. В третьем же всё нормально: TypeError, так как строки и числа не сравниваемые.

Полностью поддерживаю, хоть и написано несколько в грубой форме. 90% моментов описанных тут из-за не знания языка. Пример с (2,4,5,6) === (2,5,6) вообще смешон. А чего автор ожидал от такого сравнения? Ему не знакомо как работает оператор, или оператор ()? Вот этот пример очень хорошо отображает суть статьи — чем меньше знаний о языке имеешь тем больше моментов тебе (считаешь глупыми) не понятны. Повторюсь с остальными так же. Везде есть объяснение.

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

Мне только не ясен пример с коммутативностью. Плюс параллелизм в JS я не особо знаю, но вроде он не сильный — да. Остальное всё просто школьные выдумки о якобы «плохом языке», а на самом деле о слабых знаниях автора, либо же просто желание автора поджечь очередной холивар.
var re = /a/g
re.test('ab')
// true
re.test('ab')
// false
re.test('ab')
// true
Проверил — реально так и есть. Кто-то может объяснить в чём цирк? Документация ничего такого не предполагает.

https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex


Если свойство lastIndex больше длины строки, методы test() и exec() завершатся с неудачей, а свойство lastIndex будет установлено в 0.

Кстати, по вашей же ссылке:


Как и при вызове метода exec() (или при совместном с ним вызове), метод test(), вызванный несколько раз на одном и том же экземпляре глобального регулярного выражения, будет начинать проверку с конца предыдущего сопоставления.
Ах вот оно что. Спасибо что объяснили. Я, вообще, люблю JS, но это какая-то дичь…

Просто статья завалена бредовыми примерами. Вот это вообще какой-то угар:


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

typeof([1][0])
// number
for(let i in [1]){console.log(typeof(i))}
// string

Начнем с того, что typeof это оператор, поэтому не совсем понятно зачем он там юзает скобки. Ну это косметика, не важно.
Важно то, что в первом случае он делает


typeof 1 // что логично - намбер

Во втором он делает


typeof "0" // так как итерирует над ключами объекта, а первый ключ объекта массива это "0".

Что можно вообще думать о познаниях автора?


Следующий пример не лучше же — сам дергает toString (ну ему это, конечно, не известно, так как зачем читать как работает ==) — и удивляется, что переменная то меняет свое значение.

Что можно вообще думать о познаниях автора?

Мне кажется, что с познаниями у автора всё нормально. Просто нужен был материал для статьи :) Всё равно те, на кого эта статья ориентирована, не полезут разбираться во всех этих for-of, for-in, for;; и т.д… А значит пример задаче пригоден.


Очень немногие пункты из всей статьи по факту являются чем-то… спорным или глупым. В большинстве своём просто мелкие нюансы инструментов, используемых не по делу. Хорошо хоть массивы с объектами не стал складывать.


P.S. всякий раз когда мне приходилось залезать в C++ у меня бомбило похлеще Хиросимы. Но я не бежал писать статью про всякие глупости.

Хм… Знали бы вы, как бомбит от C++ людей, которые реально на нём пишут :-D
И, тем не менее, пользуемся.

P.S. всякий раз когда мне приходилось залезать в C++ у меня бомбило похлеще Хиросимы. Но я не бежал писать статью про всякие глупости.

А кто-то пишет

Да тут вся статья, как и всегда в подобных случаях кстати, основана на примерах, которых в реальном мире и не встретишь. Ну может в очень специфичных местах или по незнанию. Не сталкиваются реальные люди со всей этой дичью настолько часто, чтобы это мешало.
А если еще и с typescript, то все эти приколы встречаются примерно никогда.
Здесь в js плохо то, что если работаешь с массивом, в котором элементы пронумерованы от 0 до N, и по-другомы быть никак не может, то ожидаешь что эта прогрессия будет иметь числовое выражение, а не строковое.
Я однажды потратил часов пять на поиск бага, связанного с этим поведением.
После чего сделал шпаргалку из косяков JS и перед тем как искать проблему в своем коде, сверяюсь с этой шпаргалкой.
И знаете что? Стало реально проще работать! Предположение, что это не я дурак, а автор языка, стало экономить просто прорву времени.

Всё же использовать for-in не имея ни малейшего представления о том, что это такое и жаловаться, что оно работает не так как ожидал, это странновато, нет? :)


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


А если с динамически типизированными языками до этого в целом неработали (будь то ruby, php, js, python) то слепое следование своим привычкам, скажем, из Java, C#, C++ очень часто будет приводить к печальным следствиям.


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

for-in в Python и в Ruby работает как раз так, как автор ожидает.
Т.е. дело не в динамически типизированных языках в целом, а в неочевидном синтаксисе конкретно JS.
for-in в Python и в Ruby работает как раз так, как автор ожидает

Беру пример из документации:


fruits = ["apple", "banana", "cherry"]
for x in fruits:
  print(x) 

результат: apple banana cherry
что я ожидал из ваших слов: 0 1 2


Т.е. работает он как for-of в JavaScript-е:


const fruits = ["apple", "banana", "cherry"]
for (const x of fruits) 
  console.log(x) 
// apple banana cherry

Может быть вы имели ввиду другую конструкцию?
P.S. ruby проверять лень, давайте остановимся на python-е

Так автор и ожидал поведения, как у for-of в JS.

Это очень плохо характеризует автора. Писать код по принципу "я так писал в языке X, если оно не упадёт с syntax error, значит должно работать точно также"… В общем логика тут немного безумная. В большинстве языков вообще будет syntax-error.

Я же говорю: дело не в динамически типизированных языках в целом, а в неочевидном синтаксисе конкретно JS.

Конструкция for-in ожидаемым образом работает в большом числе языков — кроме Python и Ruby, ещё как минимум в батниках и шелл-скриптах; и в одном лишь JS она означает нечто иное.

В том же Go, с которого "переходил" автор, не всё так однозначно. Угадайте, что выведет этот код?


arr := []int{2, 1, 0}
for x := range arr {
    fmt.Printf("%d\n", x)
}

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

Выведет ключи, что-то вроде for in в JS. Не сложно и понятно

А в JS это самое — сложно и непонятно

Вот безумная логика:
x = [1,2,3,4,5];
for (var i in x) {
console.log(x[i+1]);
}
Думаете, будет 2 3 4 5 undefined?
Это совершенно естетсвенно так думать.
Но увы.

Думаю, что i — строка. Правильно думаю?

А если с динамически типизированными языками до этого в целом неработали


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

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


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

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


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

На самом деле я не думаю, что на этот вопрос можно дать объективный ответ. К тому же всё очень сильно зависит от вашей команды. Если писать такие абстракции которые только их автор и может поддерживать, то это одна история. Если эти абстракции, заключая в себе, много сложности, в значительной степени упрощают весь остальной код, а сами хорошо протестированы, то это другая история… И переменных в этом уравнении куда больше двух. Так или иначе они сужают bus-factor. И… вызывают привыкание.


Могу лишь сказать, что в отрыве от задач бизнеса, опираясь скорее не собственные хотелки, их писать очень приятно. Этакий challenge за challenge-ом. Люблю нетривиальные задачи. А вот рутинное копи-пейст программирование нагоняет тоску.

Этакий challenge за challenge-ом


В кровати, хате и халате
Покой находит обыватель.
А романтик все снует,
В шестеренки хер сует.

(с) Губерман
Не имея больших оков, и не имея нужды доказывать всё компилятору, оказывается очень удобно работать со всякими сложными абстракциями.

Например?

У меня есть задача на Шарпах, которую я не могу решить полностью статически. Допустим, я хочу получить такой контракт:


class IListener<TEvent> {
    void On(TEvent ev);
}

class MyClass : IListener<EventA>, IListener<EventB> {

    public void On(EventA ev) {
        // EventA handler
    }

    public void On(EventB ev) {
        // EventB handler
    }

}

class Emitter {}

public class App {
    public static void Main () {
        var emitter = new Emitter();
        var myClass = new MyClass();

        emitter.Subscribe(myClass);
    }
}

Я могу написать Emitter без рефлексии, который позволит мне делать следующие вызовы:


if (condition) {
  emitter.Fire(new EventA())
} else {
  emitter.Fire(new EventB())
}

Но не могу написать следующий код:


var ev = condition
  ? new EventA()
  : new EventB();

emitter.Fire(ev);

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


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


А вообще такие вещи визиторами обычно делаются, с OnEventA/OnEventB

В общем, это нормальная ситуация.

А я и не спорю. Я вообще не представляю, как статически это реализовать. По сути оно сводится к такому:


class MyClass : IListener<EventA>, IListener<EventB> {
    public void On(EventA ev) {}
    public void On(EventB ev) {}
}

var ev = condition
  ? new EventA()
  : new EventB();

new MyClass().On(ev);

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


А вообще такие вещи визиторами обычно делаются, с OnEventA/OnEventB

Понимаю. Но немного ни к чему обязывающей рефлексии — и у меня в два раза меньше кода.

Так а зачем тут рефлексия? Чем это лучше чем


class MyClass : Listener {
    public override void OnEventA (EventA ev) {}
    public override void OnEventB (EventB ev) {}
}

IEvent ev = condition
  ? new EventA()
  : new EventB();
new MyClass().On(ev);

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

А если у вас 1000 эвентов, а листенер должен слушать только 3 из них?


Плюс ваш код не работает

Оверрайдите столько методов сколько эвентом собираетесь слушать.


Плюс ваш код не работает

В каком плане? Это пример, а не полноценное приложение.

Ну я понял, что пример. Вы там метод On вызываете, которого нету… Он делает пример, увы, непонятным.


То есть Listener — такой огромный класс с тьмой методов на каждый эвент? И добавляя эвент я должен зайти в листенер и добавить ещё метод в него, правильно?

Да, так и делают в таких случаях. Необходимость написать один switch (который причем студия даже сгенерировать может) не перевешивает кучу плюсов от такого. У меня не один проект был, где люди пожидились на такой свитч, и я потом днями искал кто что создаёт, потому что через рефелкшн чето куда-то пролезает, куда-то ещё передается, и хрен что поймешь. Особенно, если проект для билда и запуска требует какой-то хитрой магии, поэтому локально не работает, и просто подебажить нельзя.

Не, у нас нету проблем. Рефлексия очень простая и всё очень надёжно и понятно. А в клиентском коде всё очень статично.

И как выглядит решение с рефлексией?

Пишу по памяти. Рефлексией создается только Invoker эвента. Что-то вроде такого класса:


class InvokerOfMySpecificEvent {
  public void TryInvoke (object listener, MySpecificEvent ev)
  {
    if (listener is IListener<MySpecificEvent> target) {
      target.On((MySpecificEvent) ev);
    }
  }
}

Теперь при Fire события ищется его инвоукер и событие вызывается на всех подписчиках.


private interface IInvoker {
  void TryInvoke (IListener listener, IEvent iEvent);
}

private class Invoker<TEvent> : IInvoker where TEvent : IEvent {
  public void TryInvoke (IListener listener, IEvent ev)
  {
    if (listener is IListener<TEvent> target) {
      target.On((TEvent) ev);
    }
  }
}

public class Emitter {
  private readonly List<IListener> listeners = new List<IListener>();

  public void Subscribe (IListener listener)
  {
    listeners.Add(listener);
  }

  public void Unsubscribe(IListener listener)
  {
    listeners.Remove(listener);
  }

  public void Fire( IEvent ev )
  {
    var invoker = GetInvoker(ev);

    foreach (var lnr in listeners) {
      invoker.TryInvoke(lnr, ev);
    }
  }

  private readonly Dictionary<Type, IInvoker> invokers = new Dictionary<Type, IInvoker>();

  private IInvoker GetInvoker (IEvent ev)
  {
    IInvoker invoker;
    var type = ev.GetType();

    if (!invokers.TryGetValue(type, out invoker)) {
      var InvokerClass = typeof(Invoker<>).MakeGenericType(type);
      var invoker = (IInvoker) Activator.CreateInstance(InvokerClass);
      invokers[type] = invoker;
    }
    return invoker;
  }
}

На самом деле у нас событиями выступают команды в движке, потому как-то так выглядит использование в логике:


class FireCommand : Command {
  public FireCommand (Unit unit)

  public void OnExec () {
    var weaponFireCmd = unit.weapon.Fire();
    commander.Exec( weaponFireCmd );
  }
}

class CannonWeapon : IWeapon {
  public CannonWeapon(int power)

  Fire() => new FireCannonCommand(power);
}

class RocketWeapon : IWeapon {
  Fire() => new FireRocketCommand();
}

class BulletWeapon : IWeapon {
  public CannonWeapon(int mass)

  Fire() => new FireBulletCommand(mass);
}

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


class SoundManager
: IListener<FireCannonCommand>
, IListener<FireRockerCommand>
, IListener<FireBulletCommand> {

  void On (FireCannonCommand ev) {
    if (ev.power > 10) {
      Play("BigCannonSound.mp3");
    } else {
      Play("CannonSound.mp3");
    }
  }

  void On (FireRockerCommand ev) {
    Play("RocketSound.mp3");
  }

  void On (FireBulletCommand ev) {
    if (ev.mass > 200) {
      Play("BulletSound.mp3");
    } else {
      Play("SmallBulletSound.mp3");
    }
  }

}

Ну и где-то при инициализации саунд-менеджера:


class Installer {
  void Install () {
    // bla-bla
    emitter.Subscribe(soundManager);
  }
}

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


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


Ну и стек-трейс очень простой:


Commander:Exec
└─FireCommand:OnExec
  └─Commander:Exec
    ├─FireCannonCommand:OnExec
    └─Emitter:Fire
      └─Invoker<FireCannonCommand>:TryInvoke
        └─SoundManager:On(FireCannonCommand)

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


Да, кстати, ещё у нас иерархия событий красиво выстраивается в логе.


Вот пример из тестов

Простите, что картинкой, но Хабра херит разметку


Каждое из этих событий может дёрнуть эмиттер, а тот — найти листенер.

Мне всё понятно, но я и говорю: это экономия в один switch, который заменен на поиск через рефлекшн. Мне это кажется шагом назад, потому что очевидность что от чего зависит понижается. У нас сразу появляются кэши и вот это всё.


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




Могу привести более приземлённый пример. Было у меня два почти одинаковых проекта (два человека в одной компании сели писать два симметричных сервиса, только один по заказам, а другой по другой сущности, но требования были очень похожи). В одном был свитч на ~50 веток который ставил в соответствие команду и действие. В другом через рефлекшн доставались все наследники ICommand и также через рефлешн определялось, что должно вызываться. Так вот, как раз с вариантом на свитче проблем было меньше всего — новые люди когда создавали по-аналогии новые команды находили свитч через find usages и добавляли ветку, и всё такое. Вариант на рефленше тоже в целом работал, но люди часто забывали или магический маркерный интерфейс унаследовать, или еще что-нибудь, и получали разлом в лучшем случае на этапе QA, а иногда и позже.




В общем, если я вижу using System.Reflection в начале файла, у меня сразу возникает определенное сомнение в необходимости подобного. И как показывает практика — часто этого можно избежать. Я проходил этот путь: сначала рефлкшн, потом кодогенерация на Expression, в последнее время, если никуда не деваться и вот не получается выразить обычными средствами, — кодогенерация через roslyn api, такие вот процедурные макросы с немного страшноватым синтаксисом. То есть, если лень писать такие свитчи руками — пусть генерирует машина. Всё лучше, чем ошибиться в рантайме.

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

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


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


У нас так карточки создаются. Свитч в итоге проклинала вся команда, в т.ч. программисты и гейм-дизайнеры.

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

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


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

А вот тут у нас всё же умудрялись косячить + обучающий момент пропадает, человек конечно может догадаться, что там маркерный интерфейс, пойти посмотреть как он используется, и догадаться что к чему, но чаще просит конфлюенс где будет написать "а у нас на проекте вот этот магический интерфейс за это отвечает, а то — за то. Чтобы добавить такой-то функционал скопируйте тот NewCommandExample.cs и отредактируйте как нужно".


У нас так карточки создаются. Свитч в итоге проклинала вся команда, в т.ч. программисты и гейм-дизайнеры.

Забавно, что вы приняли противоположенное нашему решение. Не могу сказать, вопрос в культуре разработки, философии о назначении типизации (мне ближк идеология parse, don't validate) или просто размер команды.

Кстати, у меня вопрос, а почему не сделать что-то в духе:


class SoundManager : IListener {
  void On(IEvent event) {
    switch (event) {
      case FireCannonCommand ev:
        if (ev.power > 10) {
          Play("BigCannonSound.mp3");
        } else {
          Play("CannonSound.mp3");
        }
        break;
      case FireRockerCommand ev:
          Play("RocketSound.mp3");
        break;
      case FireBulletCommand ev:
        if (ev.mass > 200) {
          Play("BulletSound.mp3");
        } else {
          Play("SmallBulletSound.mp3");
        }
        break;
    }
  }
}

По сути у вас АДТ в IListener, ну и вы обрабатываете те что интересно.

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


Вы, кстати, не ответили:


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

Я вот ещё что вспомнил, у меня был вариант полностью статического подобного эмиттера:


public class IEvent {
  void Notify (Emitter emitter);
}

public class Emitter
{
  private readonly List<IListener> listeners
    = new List<IListener>();

  public void Subscribe (IListener listener)
  {
    listeners.Add(listener);
  }

  public void Unsubscribe(IListener listener)
  {
    listeners.Remove(listener);
  }

  public void Fire<TEvent>( TEvent ev )
    where TEvent : IEvent
  {
    foreach (var lnr in listeners) {
      if (lnr is IListener<TEvent>) {
        lnr.On(ev);
      }
    }
  }
}

public class Commander
{
  // blabla

  void Exec (ICommand cmd) {
    cmd.OnExec();
    cmd.Notify(emitter);
  }
}

interface ICommand : IEvent {
  void OnExec();
}

public class MoveCommand : ICommand
{
  public void OnExec () {
    // bla-bla
  }

  public void Notify (Emitter em)
    => em.Fire(this);
}

public class AttackCommand : ICommand
{
  public void OnExec () {
    // bla-bla
  }

  public void Notify (Emitter em)
    => em.Fire(this);
}

Но, были причины, почему оставили рефлексию.

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

Ну, с этим я могу согласиться.


Вы, кстати, не ответили:

Ответил, смотрите первое предложение: https://habr.com/ru/company/ruvds/blog/499670/#comment_21567940


Я вот ещё что вспомнил, у меня был вариант полностью статического подобного эмиттера:

Тоже неплохой вариант, и ничуть не менее декларативный, особенно если чутка переписать:


foreach (var lnr in listeners.OfType<Listener<TEvent>>()) {
  lnr.On(ev);
}

Далеко не любой язык со статической типизацией умеет в high order rank polymorphism, или алгебру типов. В свою очередь, чтобы обойти ограничения ты или cast-ишь (убегая от типов), либо копипастишь (или даже кодогенерируешь), или используешь рефлексию (ох...), или просто не используешь какие-то прикольные фишки. Чаще даже не догадываешься об их существовании, т.к. в материалах про твой язык тебе они не попадались, в коде библиотек с которыми ты работаешь — ничего подобного нет, а всякие там книжки про паттерны проектирования выглядят как-то очень надуманно.


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


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


Ну ты понял. Просто представь что ты 10 лет писал на Idris-е, а потом тебя посадили на pascal образца 90-х.

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

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

  • Java
  • Kotlin
  • Go
  • C#
  • Swift
  • Rust
  • C
  • C++

Ну вот я взял из списка самых популярных языков в 2020г (первая ссылка в гугле) те что со статической типизацией. Ни Scala ни Haskell тут нет. Вопрос в каких из этих языков есть типовая алгебра, сужение типов в if-ах и condition-ах, полиморфизм высшего порядка?


Ну так из этого ведь не следует, что статическая типизация плохая

С этим соглашусь, да.

Вопрос в каких из этих языков есть типовая алгебра, сужение типов в if-ах и condition-ах, полиморфизм высшего порядка?

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


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

Если честно, то я не смог распарсить второй абзац :)
Но на всякий случай повторю — я не против статически типизированных языков (я за). Но я бы предпочёл динамически-типизированный язык тем из них, которые имеют слабую недоразвитую систему типов. Вотъ.

И правда, как-то сумбурно получилось.


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


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

Если честно, то я не смог распарсить второй абзац :)

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

О, спасибо за перевод. Нет, это не так. К примеру у нас на backend-е scala. И вроде как по итогу выбор скалы рассматривается как ошибка, т.к. очень сложно найти и нанять scala-разработчика. И не из любого java разработчика удаётся сделать scala-разработчика. Техлид говорит, что если бы он вернулся назад во времени то выбрал бы какой-нибудь go или java.


Точно так же и я не могу взять для нового проекта какой-нибудь elm, потому что с точки зрения бизнеса это будет фиаско. Мы с огромным трудом даже javascript разработчиков находим, а на elm не найдём никогда.

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

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


Скажем моим хотелкам удовлетворяют всего 3-4 города в Европе. Мюнхен и города Швейцарии. Будь я каким-нибудь Haskell программистом, насколько сократился бы мой выбор? :) А JS/TS разработчики нужны в любой дыре. И работы им лет на 20 вперёд заготовлено.


Думаю у вас в США с этим получше (как и с IT в целом). Но тут уже сама страна и её бюрократические препоны вносят немалый вклад...

Осталось понять, что лучше — хаскель-разработка в Москве или тс-разработка в Мюнхене )

К примеру в моей конторе пишут на Objective C, на Swift, на Kotlin и на Scala. Даже Scala (2-ая) не умеет того, что умеет TypeScript.

Я как раз недавно писал хабрапост про артефактные цвета в CGA, и мне нужно было сгенерировать таблицу возможных цветов и соответствующих им композитных сигналов.
Эта программа, скорее всего, никогда больше никому не понадобится.
По-вашему, я её всё равно должен был писать на языке, позволяющем «читать, понимать и развивать, в том числе и тем, кто ее первый раз в жизни видит»?
Да для себя можно писать на чем угодно равно как и заниматься любым извратом. Речь идет о коллегах, нынешних и потенциально будущих, а не о мастерах по созданию произведений write&dump.
То, что я писал генератор для таблицы цветов на языке с динамической типизацией — это изврат?
Повторюсь — я писал про промышленное производство, а не про поделки для себя. Для себя можно хоть на заборе, хоть на brainfuck.

Нет, почему? Задача ж решена.


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

for...in — это самый удобный перебор в js.
У .foreach и for слишком много неудобств и ограничений.

Простите, а вы точно знаете чем отличается for-of, for-in, for;;, forEach? и знаете про методы .entries, .keys, values?

Здесь в js плохо то, что если работаешь с массивом, в котором элементы пронумерованы от 0 до N, и по-другомы быть никак не может, то ожидаешь что эта прогрессия будет иметь числовое выражение, а не строковое.

В JS ключи бывают только строковыми. То, что вы можете искать и по цифрам — это динамическая природа языка. Это отвечает на много вопросов)

Это нормальное поведение тех же перловых регекспов со /g (привет PCRE):
$ perl -le 'my $a = q{abcabdabe}; print $1 while ( $a =~ /(ab.)/g );'
abc
abd
abe

Без /g этого не происходит.
s/нормальное/традиционное/
цирк в том, что регэксп это на самом деле объект с состоянием. а /a/g это короткая запись его конструктора.
А если не хочется беспокоиться что там у нас в состоянии находится нужно просто создать новый регэксп.
Ну и вот это мне кажется очень странным. В моём понимании регэксп это детерминированный тест, который всегда возвращает одно и то же, при одних и тех же параметров. Мне сложно придумать ситуацию когда бы мне понадобилось состояние у регулярки…
Ну да, сделано не очень понятно. Но это хорошо известный прикол и в документации упоминается.
вызванный несколько раз на одном и том же экземпляре глобального регулярного выражения, будет начинать проверку с конца предыдущего сопоставления.


Обычно, чтобы не следить за этим, используют методы строки, куда передается регэксп, т.е. String#match и String#search.
Заметьте, что в данном примере мы как раз вызываем методы самого объекта регэксп.

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

В моём понимании регэксп это детерминированный тест, который всегда возвращает одно и то же,

'somestring'.match(/someregexp/)

Вот так вот используют RegExp-ы в JS практически всегда. И да, оно работает именно так, как вы описали. А то что в статье, это… ну такой более низкоуровневый примитив для хитрых нужд.

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

Простите, но я не увидел ответ на вопрос. Логично, что если Вы совершаете манипуляции с числами в рамках одной системы — проблем с Endian не будет. Но если это массив данных с другого устройства, где архитектура работает на BigEndian, то у Вас очевидно будут проблемы. Дело в том, что Вы не всегда определяете архитектуру системы. Например, MIPS архитектура полностью построена на BE и является практически стандартом на российском рынке импортозамещения. На мой взгляд, Вы не закончили мысль.
Предполагается что ответ «А хрен его знает» :)
Например, MIPS архитектура полностью построена на BE и является практически стандартом на российском рынке импортозамещения.

MIPS может работать в LE/BE режимах.

К примеру PS2 — LE, IRIX — BE.
Байкал — T1 использует LE.
Из всего этого списка нежданичков пример
const x={
  i: 1,
  toString: function(){
    return this.i++;
  }
}

if(x==1 && x==2 && x==3){
  document.write("This will be printed!")
}

совсем несправедливый.

Если в языке можно перегружать какие-то операторы, то очевидно, что там можно получить дичь. В питоне тоже можно сделать
class Foo:
    def __eq__(a, b):
        return random() < 0.95 or os.system('rm -rf /')

И в плюсах. Да где угодно.
Тут как бы не оператор сравнения переопределили а преобразования к строке. Ни в питоне ни в С++ такой оператор не влияет на сравнение.
а какая разница? Без чтения кода класса вы никак не узнаете, что переопределено.

Справедливости ради, если не определен метод __hash__, то именно преопределенный __repr__ или __str__ и вызовется при попадании в словарь. Или, если не ошибаюсь, при сравнении с ключом словаря. Надо проверить, как у компа буду

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


Поясню, js-код выше это непонятное нечто. У нас есть объект, в нём есть одно поле. И мы зачем-то нестрого сравниваем этот объект с числом. Приведение к числу мы не описали, поэтому система сваливается до сравнения строк. Приведение к строке мы написали, причём таким образом, как будто хотели устроить диверсию. Ну… диверсия получилась.


Однако в воздухе витает вопрос, а на черта это всё было сделано? Аналогия с переопределением __eq__ более чем корректна. В JS просто нет __eq__, а есть пара других методов.

Начали вроде весело, а продолжили очередными сравнениями. Да, давайте в 20ый раз посмеемся


var a = "41"
a += 1
// 411, неправильно, несбалансированно, странно.
var b = "41"
b -=- 1
// 42

АХАХХАХАХАХА.

Perfectly balanced. As all things should be. Ну а вообще, обьединить Таноса и ответ на «Главный вопрос жизни, вселенной и всего такого» — это мощно.
Мои пять копеек: gofmt и go fmt – это разные команды. Wat???!!!

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

Я к тому, что прежде чем бросать камни, надо быть уверенным, что сам не без греха.
Кривость одной технологии действительно исправляется кривизной другой?
Абсолютно нет, и про все косяки JS много раз обсусолено. При чем, если от каких-то никакого вреда нет, если писать просто хороший код, и они являются лишь поводом для таких статей и нападок, то есть фундаментальные проблемы — например с целочисленными типами. Но эта статья подается как «я пришел из мира go, и сейчас расскажу как все отстойно в js».
Почему любая критика любимой технологии воспринимается как «нападки»? Любой человек меняя один язык на другой руководствуется в первую очередь своим багажом опыта и сравнивает. Находя или не находя известные паттерны учиться проще. Вполне естественно, что «норм» для варящихся в своём соку джаваскриптеров может оказаться «не норм» для гофера-свитчера. Так и наоборот! Но бестолковые для технического спора выпады в стиле: "… надо быть уверенным, что сам не без греха." и прочие переходы на личности в комментариях вокруг явно показывают, что люди в самом деле оскорбляются, принимая критику на свой счет.
Что заставляет людей настолько прирастать душой к инструментам, делая из них субкультуру? Боязнь разочарования в собственном выборе?
Это что, вон в питоне взяли на 3ю версию тупо заменили все strings на utf8 и сказали пляшите как хотите.
Как раз тот случай когда ошибку изначального дизайна исправили, хоть и ценой поломки совместимости.

Чушь. Ничего не исправили они. Проблем с этим не было изначально ибо был unicode(). Просто кто-то сказал "давайте будем прогрессивными" и сделали utf8 дефолтом. Даже не думая, что во всех других системных языках utf отдельно идёт (что правильно ибо подсистемы все (сеть, диски, память) работают в C локале.).


В общем разом убили простоту питона в угоду новомодью. При этом проблемы с тредингом до сих пор не решают чтобы "не ломать компатабилити". Где логика? В моем компе 8 ядер, ему через год 10 лет будет! Это уже не смешно.

Строки и массивы байт семантически отличаются и их смешивать в один тип не совсем правильно (например аналог в Qt — QString и QByteArray). Преобразование между строками и байтами простое, не вижу в чем проблема. С python3 куда логичнее стало.

GIL — проблема не в стандарте языка а конкретной реализации, хоть и основной. Делать thread safe решения ударило по производительности однопоточной версии. Неприятно конечно, но тут не причина в совместимости, разве что возможно частично на уровне C API.
Строки и массивы байт семантически отличаются и их смешивать в один тип не совсем правильно (например аналог в Qt — QString и QByteArray). Преобразование между строками и байтами простое, не вижу в чем проблема. С python3 куда логичнее стало.

Qt не язык программирования, а GUI система. Если бы это было в Tk проблем не было бы.

Так выходит чтобы написать простой скрипт аля dd или tcpdump на питоне придется танцевать вокруг урезанных (по сравнению с str) bytes и bytearray. Для вывода в print данных придется делать encoding и т.д. и т.п.

Простота решения была очевидна — оставить str таким же как в других языках и сформировать новую unicode string для конкретной работы с unicode (который как выяснилось в итоге имеет свои лимиты, так что ждем python4 c ucs или utf32). И ее укороченную версию u"".

Грубо говоря качественное изменение было абсолютно негативным.

Касательно GIL — да проблема не в стандарте. Но факт что решение не было проведено в течение !20! лет говорит о том, что это не приоритет.

В итоге Python3 только недавно (5 лет) начал набирать популярность, почти уверен это связано с JS default utf16 и просто тупо дропом Python 2.7 поддержки.

Python3 тупик, который только и ждет какой-то качественной замены языком с таким же читабельным синтаксисом и поддержкой трединга. Возможно форк python2.
> Qt не язык программирования, а GUI система.
Строго говоря это «с++ фреймворк». Можно и консольные тулзы писать )

> с таким же читабельным синтаксисом
На вкус и цвет, а по мне
",".join(arr)
дичь полнейшая.
Почему не
arr.join(",")
?
Ну и прочее подобное (хорошо запомнилось только это).
Golang в разы читабельней и меньше «правил чтения» надо запоминать
Golang в разы читабельней
Это скорее маргинальное мнение (субъективность читаемости оставляет мало места метрикам). По-моему, Python хвалят за читаемость, а Go критикуют за многословность из-за бедного синтаксиса, то есть наоборот. Обилие синтаксиса не особо коррелирует с читаемостью, чаще наоборот. Brainfuck как раз основан на минимализме правил. Из менее радикальных примеров, в Lisp минимум правил и в Forth без надстроек. Тоже часто критикуемы за нечитаемость.
Почему не arr.join(",")
Выбор явно намеренный, видимо, автору это показалось более похоже на естественный английский. Читайте как "," joins array. Это, можно сказать, идеология Python. Могу согласиться, что это сомнительная цель и сомнительное решение с т.з. Computer Science.
Дело не в естественном языке: string.join принимает аргументом любой iterable.
Если бы делали наоборот, то пришлось бы определять методы join у списков, кортежей, множеств, генераторов и чего угодно ещё.
Только iterable объектов, сериализующихся в строку. Да, это было ошибочное предположение из одного из плюсов Python. Работа с Iterable[str] выглядит самым логичным выбором.
Я ещё, пожалуй, добавлю:
Обилие синтаксиса не особо коррелирует с читаемостью, чаще наоборот. Brainfuck как раз основан на минимализме правил. Из менее радикальных примеров, в Lisp минимум правил и в Forth без надстроек. Тоже часто критикуемы за нечитаемость.
Языки с избыточно богатым синтаксисом, например Perl, так же часто критикуемы за нечитаемость.
Qt не язык программирования, а GUI система.

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

Даже не думая, что во всех других системных языках utf отдельно идёт

Почему вы сравниваете Python с системными языками, а не с другими скриптовыми, вроде того же JS?
В Time смешали две совершенно, абсолютно несвязанные сущности – wallclock time, и стабильную отсечку временных интервалов, таймер.

Не очень понятно, о чем именно речь и в чем проблема.

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

Ускоряет или замедляет? Каким образом что-то должно вообще на NTP реагировать? Если оно на монотонных часах реализовано, как это корректно делается и сделано в Go, то никак не реагирует. Wallclock и NTP никак не касается этого всего. Хотя конечно даже монотонные часы имеют свои причуды в зависимости от ОС.

Таких статей уже сотни, если не тысячи. В целом, все так и есть, поэтому на проекте от 100 строк кода нужен линтер, он нескольких тысяч строк — typescript.

Javascript мой любимый язык программирования… когда нет работы и проектов я только на нем и пишу…

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


typeof([1][0])

Является код


for(let i of [1]){console.log(typeof(i))}

Который выдаёт number.

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

ЗЫ. Сам от ES6 был в восторге после многолетнего опыта с callback hell и прочим for...in...hasOwnProperty

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

Если лень прочитать пару предложений

Перерыв гору текста, туториалов, спецификаций (а где они? ага MDN раньше не было) и прочего )
Не забываем, человек не профильный в js.

Не было MDN — это когда? По-моему, он с нами уже лет 10. Все доки есть там.


А доки node.js прямо на оф сайте.

Я пересел с автомобиля на горные лыжи, и вот почему(с)

2020 год, люди до сих пор не отличают язык программирования отличают от рантайма
А уж блок где автор ругается на (x, y, z) == (...) вообще вызвал смех. Как можно жаловаться на скобочки в языке, основанном на лиспе?

Но ведь это выглядит как список/кортеж, а не progn.

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


Всё субъективно…

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

Традиция записывать возведение в степень с помощью ^ — намного старше JS, да и программирования вообще.

JavaScript основан на Scheme, который основан на Lisp…
И квадратные скобки для списка только в Haskell, а в других языках и синтаксиса для списков нету
Массив это не список если что )

Сам пишу на го, но из-за аспектов (нет наследования, дженериков, явных интерфейсов, ещё что-то xD), которые заставляют писать один и тот же код несколько раз за небольшой объем кода (на 100 строчек кода из них будет около 30% (если не больше) воды (повторений, которых можно было бы избежать)), я начал смотреть тайпскрипт. Сейчас использую гошку только для реализации небольших программ (не более нескольких сотен строк, и то, если необходима именно скорость), а для чего-то побольше использую js (typescript). К тому же, использовать воркеров в ноде гораздо безопаснее, чем использовать горутины в го, имхо.

Наследование можно заменить композицией.

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

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

Эмм, нет. Для того, чтобы это понять достаточно знать определения (зависит от логики человека, который читает определения). Знать где и что используется. Иметь опыт использования...

И в каком же случае композиция не может заменить наследование ?


type Obj struct {
  p: ParentObj
}
func New() *obj {
  return &Obj{
    p: ParentNew()
  }
}

func (o *obj) IWantToInherit() {
  o.p.ParentFunc()
  // some other logiс
}

не ?


Согласен, что то писать придется, но точно не 100500 строк или 30%

И в каком же случае композиция не может заменить наследование ?

А ну только если в качестве типа входных/выходных хочется указать базовый класс. Но для этого есть интерфейсы.

Но тут вступают в бой неявные интерфейсы… Благодаря которым появляется множество неочивидных мест, которые не может отловить компилятор…
Да даже когда ты хочешь использовать обычный DI, тебе приходится писать очень много различных фабрик, которые можно было бы избежать в том же js.

Но тут вступают в бой неявные интерфейсы…

Да блядство — делайте явные. Вас гитлер заставляет неявные делать, чтобы хрен кто разобрался ?

В го есть явные интерфейсы?

Понял про что вы. Был не прав )
В целом у народа не возникает с этим проблем, значит наверное все норм )
Принцип "не сри в свой код" мне в принципе нравится )

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

Ну это просто не знание языка по сути ) Все легко и просто решается.

Очень абстрактный ответ. Жаль, что го в абстракции не силён...

Ну это просто не знание языка по сути ) Все легко и просто решается.

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

Ну наверное вам просто не нужен DI при решении задачи на go (обычно так и бывает). Не нуж0н очередной фреймворк. Написал тулзу и забыл. Микросервис и все такое. Пакет utils никто не мешает соорудить (но скорей всего от него откажетесь потом)
Честно говоря я считаю тех кто использует прототипированное наследование в js извращенцами.
Написал гору (не повторяющегося) кода без наследования и ниче )

Ну так покажите. Где же?


Написал гору кода без наследования и ниче )

При чём тут наследование? Вы знаете, что такое DI?

Ну так покажите. Где же?

Сорри не могу.


Вы знаете, что такое DI?

Инжекция зависимостей. Ну честно ни разу о ней не думал когда писал на go. И неплохой код получался...

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

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

Что по вашему "не мелко" — что то с GUI? Тут да, go сосет.


Чем же вам так насолило прототипное программирование?

Полной не логичностью. Еще скажите что сахар типа class просто так ввели )


ЗЫ. Докер, кубер — это все мелко ?

"Не мелко" — либо объем кода либо кол-во интфейсов/классов. Вот у меня есть проект на TS, где 50+ классов/интерфейсов, строк кода несколько тысяч или больше (сомневаюсь, что больше 10к, я не веду учет), и я не представляю как это бы все смотрелось на Го. Скорее всего на го кода было бы в полтора раза больше.
Насчет прототипного программирования вы говорите слишком абстрактно, возможно, вы в этом не разбирались

"Не мелко" — либо объем кода либо кол-во интфейсов/классов

Ну вот есть докер с кубером. Понимаю что там может быть написано все "вопреки", но сомневаюсь.


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

Именно! Я не считаю нужным разбираться в мутных вещах )
А тем более использовать их.


ЗЫ. String.prototype.OLOLO и ололо? )

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

Мдя, без желания узнать как и что устроено хорошим разработчиком трудно стать.

Извиняюсь но не в случае с js. Там я знаю то что мне нужно (не используй колбеки, опасайся for...in и тому подобное. Не используй наследование :)).


А вы видели исходники докера/кубера?

Нет, но давайте с вами разберем, если хотите. Просто не считаю это "мелкой" программой вот и все.


А если вы про НАЛИЧИЕ исходников — все на гитхабе есть.


Извиняюсь но не в случае с js. Там я знаю то что мне нужно (не используй колбеки, опасайся for...in и тому подобное. Не используй наследование :)).

Эту мысль можно было бы развернуть, но я не буду )

А вы знали, что суффикс "сь" указывает на говорящего? =) Т.е. вы извиняете себя =)
Что-то думать (не считать "мелкой" программой) и приводить в качестве аргумента — не одно и то же

А вы знали, что суффикс "сь" указывает на говорящего? =) Т.е. вы извиняете себя =)

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


Что-то думать (не считать "мелкой" программой) и приводить в качестве аргумента — не одно и то же

Тут вообще какая то дич не понятная. Что хотели донести? Что я Хуй ?

Насчёт "сь" — запоминающееся событие в школе. Поэтому обратил внимание =)
Но на самом деле это ещё раз иллюстрирует то, что вы не разбираетесь в тонкостях того инструмента, которым пользуетесь =).

Друже, в "тонкостях" я разбираюсь. И эти тонкости мне не всегда нра. О том и речь. А ты походу выпил уже (как и я :))

Если обидел, извини.
Но меня часто выбешивает использование чего-либо в популистическом смысле (когда не разобрался, а говоришь лишь бы украсить высказывание).
Не, у меня 5 час утра

Если обидел, извини.

Обычно меня можно обидеть, если сказать что я в чем то не шарю ) Ну впринципе ты так и сказал, но я не зол )
Я никогда себе не позволяю что то сказать о том в чем не разбираюсь )

С учётом того, что в мире много всего, чего вы можете не знать, обидеть вас довольно легко =)
А вообще в таких случаях действует простое правило: "Не стыдно не знать, стыдно не учиться". По крайней мере я так говорю своим знакомым и на них это работает =)
Триггер у меня сработал на ваше высказывание про докер, т.к. часто это вижу, и вижу что люди не разбираются в этом. В том, какие трудности были, в том как это работает изнутри.

С учётом того, что в мире много всего, чего вы можете не знать, обидеть вас довольно легко =)

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


В том, какие трудности были, в том как это работает изнутри.

Мне кажется это не было связано с языком. Но могу ошибаться конечно )

А методы можно заменить процедурами.

Это ведь тоже не работает {property: «value»} && {property: «value»}

Из-за меток. Да в JS есть метки. И в некоторых сложных ситуациях возникает неоднозначность для парсера — тут блок кода с метрой внутри или тут объект с полем внутри. Метками никто не пользуется, но ввиду обратной совместимости они всё ещё поддерживаются.

&& или || в js просто выполняет выражения по порядку в зависимости от результата предыдущего и возвращает результат в зависимости от условия
Вспоминаем например установку дефолта
val = val || 'I AM DEFAULT'


В случае с фигурными скобками просто парсер ломается, оберните в скобки и вуоля.
({property: «value»}) && {property: «value»}
Тоже самое с IIFE

function(){
  console.log('SYNTAX ERROR')
}()

(function(){
  console.log('THIS IS FINE')
})()

При этом


!function () {
  console.log('fine')
}()

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

Ну не хотел в причуды парсера впадать ) А по сути это "обман парсера" (чудный js)


Я бы такое не использовал. Используйте только то что понимаете как работает.

О каком обмане идет речь?


Правило-то очень простое — по дефолту функция понимается как FD. FD это Function Declaration, на который, например, применяется hoisting. Так вот по скольку это "declaration" — это является стейтментом. Стейтмент это управляющая конструкция — такая, например, как for/if/throw и так далее. А вот Function Expression это нечто совсем другое. И для того, что из FD сделать FE — надо явно задать контекст — то есть например использовать какой-либо оператор (оператор применяется только и только на expression'ах — поэтому функция понимается как expression и еррора нет).


А уж что вы используете за оператор — () или! или любой другой — не имеет никакой разницы.

Правило-то очень простое ....

Вы правда думаете это проще запомнить чем "парсер глючит на фигурных скобках" ?


О каком обмане идет речь?

Элементарном. Он там че то не может переварить, дай ему переваривариваюмую пищу — не обман?
IIFE это по сути в js изобретено было кстати ) Обманом парсера )

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

Вы правда думаете это проще запомнить чем "парсер глючит на фигурных скобках" ?

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

Парсер работает не так как ты думаешь — он глючит
Почему глючит — да вообще пох
Как то так.


Мне хватает и в самом языке ньюансов чтобы еще с парсером разбираться.

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

Попробуйте вот с этим решить РЕАЛЬНУЮ задачу.

Лично я не сторонник goto. Моя б воля я бы убрал goto из большинства языков. Но в большинстве старых языков goto есть. JavaScript не исключение.

Причем тут goto? И да в ядре линукс используется )
Вы понимаете, что дичь несете порой ?

Почитайте что такое метки и попробуйте сами догадаться почему я сравниваю их с goto. Идеологически это всё примерно об одном и том же. О том почему goto зло и является ли он злом была уже не одна сотня холиваров.

Вы понимаете что вы зациклены на метках?
Я знаю как они определяются. И вы заметили слово и двоеточие…
Это НЕ НОРМАЛЬНО.

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


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

Стёб стёбом, но JS прекрасно выполняет те задачи которые на него возложены.
При желании всегда можно найти за что зацепится, но в мэйнстрим все эти сложности просто не существуют. Что да существует это программисты которые пытаются съесть суп вилкой… но это уже не проблема конкретного языка
Не в контексте Go, наверное, но в целом есть замечательная фраза:
«Есть языки, которые все ругают, а есть те, на которых никто не пишет».
А вообще, современный JS на мой скромный взгляд, — это уже своего рода «ассемблер» на фоне целой кучи языков и диалектов со статическим анализом и вот этим вот всем, которые в него транслируются, если это нужно.

Несколько лет использовал Node.js. В последнее время выбираю альтернативы: Python, Erlang (Go как-то не по душе). В том числе и по тем же причинам, что описаны в статье.

Go крут, попробуй. У python дикий синтаксис, который питонщики называют легкочитаемым, а преимуществ перед go не наблюдается. Про erlang ничего не могу сказать, но изучать язык с его популярностью я бы не стал. Из всего что я юзал написаного на erlang — couchdb.

Для сравнения подходов сформулирую простую задачку: дан длинный список URL, нужно по всем ним пройтись и загрузить данные. При этом 1) это должно быть эффективно; 2) в случае сбоев загрузки, повторять попытки до достижения успеха. Знаю, как такое делать на Node.js, скрипт получается сложный и для разработки, и для чтения, но работает хорошо. Соответственно: насколько просто решить такую задачку на Go, Python, Erlang, (write here what you want)?

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

А если внезапно стало какой нибудь 404?
Но это к сути вопроса дела не имеет. В целом golang для меня как заменитель nodejs, поэтому посоветую его.
Для решения вашей задачи всего лишь нужна функция парсинга которую надо запускать как горутину (типа go parse(url)), возможно перезапускающая саму себя и механизм типа sync.WaitGroup


ЗЫ. Для меня golang как очень хорошая замена nodejs. Принципы очень похожи, а строгая типизация не дает развалится коду когда решишь поменять что нибудь в каком нибудь json )

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

Наверное есть смысл посмотреть на Go. Просто сейчас уже поизучал Python, а Erlang в процессе. Можно даже на примере такой задачки сделать сравнение нескольких языков. :)

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

Тогда уж лучше TypeScript — типизация значительно строже, чем в Го.

Бла бла бла. Мне не нужна "СТРОЖАЙШАЯ ТИПИЗАЦИЯ В МИРЕ". Мне нужно всего лишь чтобы при смене структуры json конфига (например), весь код не развалился.
И да, вы Врун. что значит "значительно строже" ?

В гоу всё интерфейсами перетыкано. Про какую типизацию может быть речь?

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


Ни разу не испытывал проблем с интерфейсами "гоу". Может они не понятны по началу, но зато удобны. В том же пхп (да хоть в с++) я вообще хз зачем нужны интерфейсы.


Ох, если вы про приведение типа из интерфейса — то это ПОЛУГАВНОКОД считай.А если ваш проект обмазан интерфейсами (мой нет), то это проблема проекта как мне кажется. Запустили гору ООПэшников, что еще ожидали ?

Ох, если вы про приведение типа из интерфейса — то это ПОЛУГАВНОКОД считай.А если ваш проект обмазан интерфейсами (мой нет), то это проблема проекта как мне кажется. Запустили гору ООПэшников, что еще ожидали ?

Этот ПОЛУГАВНОКОД™ это официально благославленный (и единственный) способ писать обобщённый код. Есть ещё один:


image


Но библиотечный код так не написать.

Мой "редактор кода" так умеет и что? ) И кстати ни разу не пользовался этой возможностью.


Но библиотечный код так не написать.

Ну значит Вам не нужно писать библиотечный код.

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

Знаю, как такое делать на Node.js, скрипт получается сложный и для разработки, и для чтения, но работает хорошо.
А можно подробнее, какие недостатки в вашем случае у тупого решения «в лоб» типа такого?
async () => {
    const urls = ['http://example.com/movies.json', 'http://example.com/songs.json'];
    const fetchUrl = async url => {
        try {
            const response = await fetch(url);
            if (response.ok) return response.json();
        } catch (_) { }
        return fetchUrl(url);
    }
    return await Promise.all(urls.map(fetchUrl));
}
(код браузерный, не нодовый)

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


(В Node.js встроенный request не Promise-фицирован, что дополнительно доставляет.)

По мне этот код сложен … в написании.
Ну, не знаю, я минут 7 потратил, наверное. Из них 1 на удаление TypeScript'овых типов (писал на TS, но мы же про JS), 2 на придумывание имён переменным, и 2 на гугление синтаксиса fetch на MDN (давно руками не дёргал).
(В Node.js встроенный request не Promise-фицирован, что дополнительно доставляет.)
Ну, уж под это наверняка уже 101 разный NPM-пакет есть =)

Да, есть. Я использовал request-promise-native. Но хотелось бы, чтобы Promise-фикация была из коробки.

встроенный request это что?
да, и в ноде есть util.promisify, довольно удобный

1) request — функция из стандартного модуля https: https://nodejs.org/dist/latest-v12.x/docs/api/https.html
2) util.promisify — не знал, честно говоря; ну это прекрасно, раз можно Promise-фицировать стандартными средствами. https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_promisify_original

А я не знал что есть стандартный реквест :)
Все как-то обходился axios. Круто, не надо ставить лишнего для моих маленьких скриптов. А это похоже на xhr. То, что оно не отдает промис это плюс, есть гибкость, а ко промису привести довольно просто.

Смотрю у axios есть дополнение retry. Это хорошо.

Интересно было бы взглянуть на ваш более легко читаемый вариант на любом языке, просто тут фактически 1 строчка кода и рекурсивный бесконечный фетч которого естественно нет в стандартной библиотеке потому, что такое использование никогда не предполагается.
document.all
// HTMLAllCollection(359) [html.inited, head, …]
document.all == true
// false
document.all == false
// false
Всё ещё смешнее.
Тут мы видим, что document.all ведёт себя как NaN.
Проверяем:
isNaN( document.all )
// true
Убедились.
Но: тут мы вспоминаем, что isNaN у нас две.
Проверяем ещё раз:
window.isNaN( document.all )
// true
Number.isNaN( document.all );
// false

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

Ага, а все потому — что это ни разу не NaN, а простой объект. Разность результатов обьясняется очень просто — это обыкновенный геттер. К тому же выпиленный из стандартов (очередной вопрос к автору — зачем обсуждать что-то, что давно уже официально убрано из даже не языка, а DOM API).


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


Естесственно с взрослением языка никто этот глобальный isNaN не использует, кроме как таких "программистов", как автор. Есть вполне давно достпный добропорядочный Number.isNaN который максимально правильно и прозрачно проверяет нан это или нет. И он нам, в принципе, правильно и говорит — это ни разу не NaN.

Но ведь object Is Not A Number )
Переименовали бы тогда уж в какой нибудь InvalidNumber

> это ни разу не NaN.
НЕ Not A Number значит Number. Но document.all НЕ Number. Еще скажите что это все «интуитивно понятно» )

Вспоминается мускульный WHERE NULL = NULL

ЗЫ. Я то это все понимаю, но…

"NaN" — вполне определённый термин. Он имеет своё определение в стандарте.
То, что вы ожидаете от этого термина поведения не из определения, а из своего странного перевода — крайне негативно о вас говорит. Ну примерно, как если бы вы написали следующее:


<i> значит "italic", а значит <i>Добрый день</i> должно перевести фразу на итальянский!

Глупо звучит, правда? Вот что-то такое вы пишете про NaN

Вот именно, тем более это к JS вообще не имеет никакого отношения, а, если уж на то пошло, к стандарту IEEE-754.

Удалил, ибо ошибся

а из своего странного перевода

NaN — вообще то не мой странный перевод, а расшифровка аббревиатуры. Еще скажите что это просто "набор букв", а не "Not A Number"


PS. ОЛОЛО
https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/NaN


Глобальное свойство NaN является значением, представляющим не-число (Not-A-Number).

Достаточно "авторитетно" ?

А <i> — это и правда italic. Что теперь?
Я сказал, что перевод странный, не расшифровка. Его нельзя переводить "как всё, что не число". Его вообще нельзя переводить. Это термин.

Вы ещё скажите, что italic переводится «итальянский».
Конечно, не поверю. Я вижу слово, которое на русский переводится «италийский».
Вы же не скажете, что «итальянский суп» это «an italic soup»?
Естесственно с взрослением языка никто этот глобальный isNaN не использует, кроме как таких "программистов", как автор.

Ах если бы...

Как-то рано автор начинает палиться: можно было бы еще несколько менее явных wtf-ов накидать вначале, чтоб потянуть интригу(=

В целом бред:


  1. В первом разделе "Система типов и работа с числами" пожаловались на:
    1. Отсутствие целочисленного типа. В реальности внутри движка для подходящих чисел используется целочисленный SMI, сейчас это int31. Для программиста тоже есть, т. к. можно целочисленно сохранить числа до 2 ^ 53 по модулю, т. к. это является подмножеством double.
    2. Отсутствие битовых операций для int16, int8 и т. д. Но они есть для int32, а меньше нужно крайне редко (мне пока ни разу за всю жизнь). Но если надо, всегда можно превратить в более маленькие числа простым отбросом старшей части (и копированием знака при надобности). Оператор "&" отлично справится с этим.
    3. % для double. Не вижу ничего плохого здесь. Используется нечасто, но и % для integer в общем-то не так часто используется, хоть и чаще. Какой смысл писать a - floor(a / b), когда можно просто a % b?
  2. Неверная сортировка по умолчанию. Это смотря с какой стороны посмотреть. Вы не задали компоратор, соответственно, движок не может знать, как Вы хотели посортировать. Хотя я бы для удобства (и скорости) добавил бы метод sortNumbers({order: 'ask|desk'}) либо расширил обычный sort до sort(cmp), где cmp — либо функция, либо строка strings_ask | strings_desk | numbers_ask | numbers_desk.
  3. (1,2,3,4,5,6) === (2,4,6) — ну оператор "запятая" то все должны знать. Самое интересное, что это очень удобно на практике. Я не скажу, что прямо часто использую этот оператор, но иногда он действительно выручает.
  4. Пример с  a = "41"; a += 1;  — за такое надо руки отрывать. Правильно так:
    var a_str = '41',
        a     = +a_str;
    a++;

    Причём в других языках требуется писать примерно также. Что логично.
  5. Можно писать код вида  ~~!![[]]||__``&&$$++<<((""??''))**00==ಠಠ--//\\  — опять же, за такой код надо руки отрывать, если это конечно не обфускация. Можно не значит нужно. Это касается всех языков — во всех языках можно написать код, за который захочется убить.
    • Аналогично и с кодом ('b'+'a'++'a'+'a').toLowerCase() — в нём явно есть опечатка, и это хорошо, что движок выдаёт ошибку.
    • Аналогично и в коде ('b' + 'a' + + 'a' + 'a').toLowerCase() есть опечатка, поэтому неудивительно, что код работает не так, как ожидалось. Правда тут, к сожалению, ошибка не выдаётся — это минус.
  6. input ?? obj?.key ? 'yes' : 'no' — вообще-то это вполне нормальный код в духе "5 + 7 / 2". Если у Вас с ним проблемы, Вы очень плохо знаете язык.
  7. «не отличаются коммутативностью» — при && если первая часть выражения вернула falsy, вторая часть не вычисляется. По-моему, это во всех языках так. С "||" похожая ситуация — если первая часть truthy, вторая часть не вычисляется. При этом возвращается всегда последняя вычисленная часть. Это максимально логично, как по мне, а самое главное — очень удобно на практике. Реально.

    А причина syntax error в последнем выражении в том, что вместо объекта Вы написали блок вида { … code … }. А потом поставили после него && — это явная ошибка. Причём в первом варианте она тоже присутствует, и на самом деле там тоже syntax error. В примере это не указано, т. к., видимо, автор выполнил код с консоли, которая пытается определить объект в начале коде. В обычной жизни в обоих случаях будет syntax error.

    При этом программист js обязан уметь отличать блоки { … code … } и объект {…}. Там, где ожидается выражение — это объект, в остальных случаях блок. Не знают этого только новички.
  8. typeof([1][0]) — снова код, за который надо отрывать руки.
    • for(let i in [1]){console.log(typeof(i))} — аналогично. Программист явно не знает разницу между for in, for of и обычным for. Также программист не прочёл спеки for in и объектов. key обязан быть строкой.
  9. «Значения могут меняться». if (x == 1 && x == 2 && x == 3) // true — потому что использовать оператор "==" нужно только для сравнения с null. Во всех остальных случаях используется ===. Не знают этого только новички. Это логично, что когда программист пишет фигню, получается фигня.
    • document.all == true / false — аналогично.
  10. «На этом он не остановился и прислал мне ещё вот это» — не нужно в качестве this передавать число, т. к. он не предназначен для этого. В данном примере число обернулось в объект, т. к. this предназначен для объектов.

    С другой стороны, лучше было бы бросить в таком случае исключение. Т. е. небольшой минус всё-таки есть. Но на практике я ни разу не встречал, чтобы кто-то передавал числа в качестве this.
  11. «Полное отсутствие в JavaScript стандартной библиотеки» — вообще не понял, что имеется ввиду под этим. Встроенное API у JS очень мощное. А в node.js из коробки идут API, в которых есть вообще всё, что может понадобиться (например, низкоуровневые функции работы с файлами или сетью). Также можно установить или подключить библиотеки (модули) от сторонних разработчиков.
  12. Код:
    for (var i = 1; i <= 2; ++i) {
    setTimeout(function(){
    console.log(i); //3
    }, 0);
    }

    Здесь всё максимально правильно, если Вы не понимаете принцип работы, значит Вы не вообще изучили язык.
  13. «0 4 3 2 1» — опять же, программист обязан знать порядок исполнения. И тут JS просто превосходен как язык. Впрочем, первое время можно обойтись и без знания — в строчках 2–4 сразу видно, что операции откладываются на потом, и достаточно просто иметь это ввиду, а в каком порядке они должны исполняться, можно изучить потом. Таким образом можно будет начать писать сразу.

    В целом это само собой разумеющееся, что раз Вы используете какую-то функцию (в данном случае setTimeout, Promise.resolve…then и т. д.), Вы должны знать как она работает. Особенно в случаях, когда это шикарная функция.
  14. Далее долго рассуждается про асинхронный код. Асинхронный — не значит, что должен выполняться в отдельном потоке. Чтобы выполнить код в отдельном потоке Вам нужно явно создать его и начать выполнять код в нём.
    • Также есть жалоба, что если начать вычислять тяжёлый код, он заблокирует поток. Так такой код нужно либо вынести в отдельный поток, либо вычислять маленькими порциями (причём каждая следующая порция должна запускаться после завершения предыдущей).
    • «выглядит громоздко» — мне кажется, или асинхронный код в целом редко бывает простым? Просто взять и начать писать его без обмена сообщениями/специальных конструкций не получится, т. к. получим проблемы с памятью. JS не исключение, поэтому в нём также приходится либо налаживать обмен сообщениями, либо юзать разделяемую память со всеми её сложностями и специальными синхронизациями.

      Про отсутствие Mutex, select и WaitGroup не скажу, т. к. не пишу такой код; мой код юзает обмен сообщениями, где не нужно думать о синхронизациях.

Но есть и адекватные претензии:


  1. Месяцы нумеруются с 0. Да, бред.
  2. Проблема с регулярками — да, т. к. по факту lastIndex вообще в них не очень нужен был, особенно с появлением итераторов. На крайний случай можно было бы передавать его как дополнительный аргумент (как это сделано в indexOf).
  3. «Он прислал мне этот прекрасный фрагмент JS-кода» — единственная проблема в нём — возможность прибавлять строку к другим типам, после чего будет сделана попытка привести эти другие типы к строке и сложить две строки. Такое автоприведение может привести к тому, что в случае ошибки вместо бросания исключения мы получим скрытую ошибка результата. На практике такое встречается редко, но возможно. Это минус языка.
  4. А также уже перечисленные выше:
    • Отсутствие удобного метода sortNumbers или расширения API sort.
    • Не выдаётся ошибка в ('b' + 'a' + + 'a' + 'a')
    • Не выдаётся ошибка при передаче неверного типа в качестве this.

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


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

PS. При этом один существенный недостаток у JS всё-таки есть: есть много вещей, в которых он не похож на другие языки. Само по себе это не является чем-то плохим (а зачастую, наоборот, хорошим, особенно в случае с JS), но если Вам нужно писать на этом языке очень мало, Вам придётся потратить кучу лишнего времени на изучение языка. При этом если Вы пишите много — это не проблема, т. к. время на изучение будет очень мало по сравнению со временем написания кода. Но тех, кому язык нужен "ненадолго", может ждать облом.


При этом с другой стороны у JS очень низкий порог входа — если Вам действительно нужно написать мало кода, Вы можете сделать это уже прямо сейчас без особого знания языка. Конструкции, описанные в статье, Вам, скорее всего, не встретятся. Всех непонятных конструкций Вы можете просто избегать. При код Ваш будет очень плохим, т. к. Вы не будете знать, как писать его правильно. Но работать код будет. Тем не менее, это не вариант, когда нужно читать или править чужой код. Здесь уже никакого "нулевого порога входа" не будет — придётся изучать весь язык.


PS. При этом есть и другие изъяны, не указанные в статье, например:


  • typeof null возвращает 'object' вместо 'null'.
  • Оператор == не нужен в языке. Даже проверку на null/undefined можно было бы заменить чем-то другим.
  • Отсутствие полностью приватных полей классов без использования замыканий (сейчас уже есть, но только с использованием синтаксиса class).
  • Приведение типов. Уже было упомянуто про "==", но приведение также срабатывает и при < >.

А также:


  • Отсутствие возможности создать полностью типизированную функцию для тяжёлых вычислений (можно поюзать WebAssembly, но это не так удобно). JS несмотря на динамическую типизацию всего-лишь в несколько раз медленнее самых быстрых языков — это не так много, но тем не менее замедление есть.
  • Отсутствие статической типизации — код работает медленнее, чем мог бы (т. к. можно было бы выкинуть многие проверки), а также такая типизация могла бы помогать отлавливать ошибки на стадии компиляции.
  • Данный язык использует сборщик мусора со всеми присущими ему достоинствами и недостатками.

В языке очень много спорных моментов. Например:


  • Я бы смело заменил prototype based OOP на обыкновенный без каких бы то ни было заморочек с this. Ситуаций когда prototype based OOP позволяет сделать что-то что другие системы не могут, и, при всём при этом, это оказалось полезным, мне так и не удалось встретить на практике. Количество же багов в реальном коде, причина которых кроется в плохообработанном контексте, не поддаётся исчислению
  • Неблочная область видимости для var. К счастью теперь у нас есть let & const
  • Архаичные метки
  • Слабая типизация вместо сильной. Я считаю это источник 80+% багов
  • Случайные глобальные переменные. К счастью это во многом в прошлом
  • null & undefined, хватило бы одного
  • слабая стандартная библиотека (я видел ваш комментарий выше, но задумайтесь почему каждая вторая библиотека тащит lodash)
  • switch с waterfall
  • до сих пор нет pattern-matching-а
  • дырявые hash-map массивы. Я думаю инженеры что пишут v8 в полном "восторге" от этой идеи
  • отсутствие каких-либо типов для чисел
  • появление NaN не приводит к ошибке
  • отсутствие многопоточности (уточню — для ноды есть свои костыли, для веба есть воркеры)
  • нет оптимизации хвостовой рекурсии (может вернут?)
  • огромный кусок API это callback-и. Promise-ы придумали относительно "недавно"

Всё выше моё личное ИМХО. На истину не претендую.

prototype based

Это субъективно. Лично я фанат prototype.


Архаичные метки

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


Слабая типизация вместо сильной. Я считаю это источник 80+% багов

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


null & undefined, хватило бы одного

Они выполняют разную функцию. Но новичкам было бы проще с одним, т. к. они не сразу понимают, в чём отличие.


switch с waterfall

Так это во всех языках так.