Comments 409
Да, хорошая саркастическая статья. Оценил юмор
Надо было до завтра дотянуть. Первоапрельская статья, первомайская… Какая разница?! )
Хорошо что это всего лишь юмор и человек останется гофером. Нам тут клоунов не нужно...
То ощущение, когда знаешь почему все это так работает, почему в этом есть логика и читаешь очередной высер дегенерата, который не способен почитать спеку или хотя бы постараться разобраться что к чему и ожидает иНтуИтИвНо ЯсНоГо яЗыкА. Тройной фейспалм.
Вот не в моих правилах вообще опускаться на личности и так бомбить, но откуда столько людей с синдромом графомании?
Как им времени своего не жалко только эти статейки калякать.
// я понимаю, что это перевод
Вопрос не "почему" оно так работает, а "какого хрена".
Ну если человек искренне считает, что вот эта конструкция в чем-то неправильна:
(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 и наконец-то ситуация стала улучшаться.
автор просто хотел выплеснуть свою желчьС этим тоже согласен. Но «желчь» подана тонко и вполне оправдана.
Но как рофл — статья действительно хороша. xd
P.S. Как поживает ваш компилятор LENS? Будут ли новые статьи на тему компиляции?
Думаю, автор намекает на то, что во многих случаях 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
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
[1,2,3,4,5,6] == [2,4,6]
// false
[2,4,6] == [2,4,6]
// false
УпсЧто значит упс? У Вас два разных объекта, с чего вдруг они должны быть равны?
Если бы это было не так, у меня были бы претензии к языку. Это было бы достаточно странно, что равны разные объекты.
А оператор "," в 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. Внутри движка на самом деле строка может изменяться для оптимизации скорости и памяти, но для программиста это остаётся прозрачным. Это внутренняя реализация, на результат вычислений она не влияет.
часто бывает нужно трактовать такие объекты как одинаковые
Дайте универсальное определение "одинаковости"
Спасибо, наглядно показывает, что в JS всё сделано максимально правильно. В принципе я говорил то же самое: JS считает одинаковым то, что всегда ведёт себя как одинаковая сущность.
2 разных объекта совсем не ведут себя как одинаковые сущности. Мы начинаем изменять объект, и если мы считали их одинаковыми, будет фейл. А, к примеру, строки и числа ведут себя как одинаковые сущности — что бы программист не делал, невозможно написать такой код, чтобы можно было отличить их, либо чтобы их различие/одинаковость привела к ошибкам в коде. Причина этого проста: числа и строки нельзя изменять. И да, это касается не только строк, но и чисел: в памяти две "5" (пятёрки) не значат, что это два одинаковых числа, в реальности скорее всего они будут продублированы, т. е. это будут две разные "5". Но программист никак не может это узнать и написать код, который бы привёл к ошибке из-за считания их одинаковыми. Всё потому, что "5" никогда не изменяется, а изменяется только значение переменной. Сама "5" так и остаётся "5" навечно.
Как итог: числа и строки считаются одинаковыми сущностями, объекты нет.
Более того: если в каких-то языках сделано не так, у меня будут очень серьёзные претензии к этим языкам.
Не очень понял, что тут не так. P выдало разные результаты, и при этом в JS 5 !== "5". Всё работает как надо.
Я так понимаю, Виталий Второй хотел сказать "числа считаются одинаковыми сущностями и строки считаются одинаковыми сущностями".
Разумеется, имелось ввиду, что, к примеру, число 5 и 5 — это в JS одинаковые сущности, как и строки "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 (да и некоторых других языков) == проверяет именно на одинаковость, на одинаоковое значение, а === на идентичность, на тожесамость.
Для скалярных типов данных одинаковость и идентичность синонимы по сути, а вот для ссылочных — нет. Два объекта могут быть одинаковыми, но === проверяет для них не одинаковость, а идентичность один и тот же объект справа и слева или разные, пускай и одинаковые.
2. Он не единственный язык, в котором нельзя вот так прямо сравнивать 2 объекта. И это логично, поскольку надо различать сравнение объекта с самим собой и с таким же, но другим объектом.
При простом сравнении идёт сравнение ссылок на объект в памяти.
Сущности слегка разные.
В js это перечисление, берущее последние значения, в шарпе-тюльпы, в питоне и делфи — хз, смахивает на сравнение массивов.
А вообще в js количество ВТФ после определённого предела сильно падает. Из последнего, что вызывало втф — работа с датами, точность при делении и, цук, неочевидное наличие this в static методах.
Но при этом всем язык очень простой в освоении и, в большинстве случаев, неплохо прогнозируемый. Потому кодить на нем, в итоге, приятно, если не усложнять себе жизнь бойлерплейтами и странными конструкциями.
Думаешь, ну емае, в js было все попроще и логичнее. :)
switch
огорчает тех, кто к нему прикипел душой.Нельзя сказать что эти мелочи прям ужасные, но таки кажутся раздражающей архаикой. В процессе работы все это сходит на нет, привыкаешь ко всему, тем не менее не могу сказать, что python прям стройнее и удобнее js.
Не хочу влазить в холивар, но в 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 имеет огромную кучу настоящих достоинств, есть ли столько достоинств в Питоне, не знаю.
а какие-то по принципу «потому что я так решил»
Явно не в данном случае, т. к. использовать запятую как оператор — это очень логично,
какие-то разрабатываются по «принципу наименьшего удивления»
Ну вообще синтаксис JS во многом взят из C и C++, и как раз наименьшее удивление, думаю, будет, когда запятая будет работать как оператор. Т. е. это не только логично, но и более привычно.
Конечно можно привести аргументы и за другое поведение запятой, но это уже на вкус и цвет… Хороший аргумент — возможность быстрого сравнения кортежей сразу из нескольких значений. Но теоретически в будущем может появиться что-то типа такого: {{5, 6, 7}} === {{6, 7, 8}}
Это всё семантика языка. Вы стараетесь его интерпретировать как-то по своему, ваши ожидания не оправдываются. Это проблема ваших ожиданий, а не языка.
Нет, если язык не оправдывает ожиданий, это может быть и проблема языка. А то так-то и с С++ нет проблем, просто у вас неоправданные ожидания что x + 1 при переполнении может быть меньше чем x.
Если все другие языки в таком виде записывают таплы, а жс решил вспомнить молодость и имеет под этим в виду что-то другое, то это беда жса, да.
а в JS это comma-оператор. Очевидно ведь
Не очевидно
Если все другие языки в таком виде записывают таплы, а жс решил вспомнить молодость и имеет под этим в виду что-то другое, то это беда жса, да.
Какие языки в 1995-м году записывали так таплы?
То JS обязательно должен был сделать этот кусок так же как в одном другом языке, который на тот момент был малопопулярен. Правильно?
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. Да в гоу не хватает некоторых вещей, но там хотя бы с логичностью все нормально.
Не может. Гоу — отвратительный и крайне нелогичный язык. Хуже всего, что он притворяется статическим, а на самом деле таким не является. А ещё у него дизайн намного сырее, чем в ЖС.
Насчет дизайна я не понимаю что именно вы имеете ввиду.
именно он накладывает больше ограничений на стиль написания, те же типы
Он накладывает ограничения слишком слабо. Из статических языков с низким порогом вхождения хороши 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 это кал говна, а не язык. Я не могу помнить всю документацию наизусть и знать что там себе нафантазировали те кто писал реализации.
Я надеюсь вы понимаете то вы спорите об 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, так как строки и числа не сравниваемые.
Пример с обфускацией тоже глупый. Обфуцировать можно любой язык. И придумать там аналогичную строку, которая будет исполняться. Возьмите хоть тот же BF)))
Мне только не ясен пример с коммутативностью. Плюс параллелизм в JS я не особо знаю, но вроде он не сильный — да. Остальное всё просто школьные выдумки о якобы «плохом языке», а на самом деле о слабых знаниях автора, либо же просто желание автора поджечь очередной холивар.
https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex
Если свойство lastIndex больше длины строки, методы test() и exec() завершатся с неудачей, а свойство lastIndex будет установлено в 0.
Кстати, по вашей же ссылке:
Как и при вызове метода exec() (или при совместном с ним вызове), метод test(), вызванный несколько раз на одном и том же экземпляре глобального регулярного выражения, будет начинать проверку с конца предыдущего сопоставления.
Просто статья завалена бредовыми примерами. Вот это вообще какой-то угар:
Типы могут меняться в самых неожиданных местах, что помогает программисту не заснуть во время долгих рабочих ночей:
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++ у меня бомбило похлеще Хиросимы. Но я не бежал писать статью про всякие глупости.
Я однажды потратил часов пять на поиск бага, связанного с этим поведением.
После чего сделал шпаргалку из косяков 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.
Конструкция
for-in
ожидаемым образом работает в большом числе языков — кроме Python и Ruby, ещё как минимум в батниках и шелл-скриптах; и в одном лишь JS она означает нечто иное.x = [1,2,3,4,5];
for (var i in x) {
console.log(x[i+1]);
}
Думаете, будет 2 3 4 5 undefined?
Это совершенно естетсвенно так думать.
Но увы.
А если с динамически типизированными языками до этого в целом неработали
А может, просто изначально не надо было делать такую херню как «динамически типизированные языки»? Ведь если тип определен в compile time, то с разбирательством что и как работает интеллектуального траходорма на порядки меньше. А программа, на чем бы не была написана — это не о «писать», это об «читать, понимать и развивать, в том числе и тем, кто ее первый раз в жизни видит».
Аргументы против динамической типизации как раз понятны. Но я думаю это топик для отдельного спора. Я тоже больше склонен к статической типизации, нежели к динамической. Но не жалею, что много лет писал именно на динамической, т.к. оказалось, что есть и свои плюсы. Не имея больших оков, и не имея нужды доказывать всё компилятору, оказывается очень удобно работать со всякими сложными абстракциями. Многие мои знакомые которые всю жизнь пишут на статически типизированных языках очень долго въезжают в некоторые абстракции, или не понимают их вовсе. Часто не понимают реальной подоплёки под многими паттернами, т.к. для них многие из них были скорее попыткой обыграть компилятор, нежели представляли решение какой-то конкретной задачи.
И вот сейчас имея Typescript со структурной типизацией и продвинутыми generic-ами я ловлю себя на том, что мне было бы физически больно писать на каком-нибудь более костном языке, где к типам прилагаются ещё и кандалы.
оказывается очень удобно работать со всякими сложными абстракциями. Многие мои знакомые которые всю жизнь пишут на статически типизированных языках очень долго въезжают в некоторые абстракции, или не понимают их вовсе
Ну, аргумент так себе. Сексом заниматься, стоя на сноуборде в гамаке тоже можно — но так ли это нужно? Вопрос в том, чем объективно эти самые «некоторые абстракции» хороши?
Вопрос в том, чем объективно эти самые «некоторые абстракции» хороши?
На самом деле я не думаю, что на этот вопрос можно дать объективный ответ. К тому же всё очень сильно зависит от вашей команды. Если писать такие абстракции которые только их автор и может поддерживать, то это одна история. Если эти абстракции, заключая в себе, много сложности, в значительной степени упрощают весь остальной код, а сами хорошо протестированы, то это другая история… И переменных в этом уравнении куда больше двух. Так или иначе они сужают bus-factor. И… вызывают привыкание.
Могу лишь сказать, что в отрыве от задач бизнеса, опираясь скорее не собственные хотелки, их писать очень приятно. Этакий 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-ах, полиморфизм высшего порядка?
Ну так из этого ведь не следует, что статическая типизация плохая
С этим соглашусь, да.
Если честно, то я не смог распарсить второй абзац :)
Но на всякий случай повторю — я не против статически типизированных языков (я за). Но я бы предпочёл динамически-типизированный язык тем из них, которые имеют слабую недоразвитую систему типов. Вотъ.
Если честно, то я не смог распарсить второй абзац :)
Я так понял 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.
Эта программа, скорее всего, никогда больше никому не понадобится.
По-вашему, я её всё равно должен был писать на языке, позволяющем «читать, понимать и развивать, в том числе и тем, кто ее первый раз в жизни видит»?
У .foreach и for слишком много неудобств и ограничений.
Здесь в js плохо то, что если работаешь с массивом, в котором элементы пронумерованы от 0 до N, и по-другомы быть никак не может, то ожидаешь что эта прогрессия будет иметь числовое выражение, а не строковое.
В JS ключи бывают только строковыми. То, что вы можете искать и по цифрам — это динамическая природа языка. Это отвечает на много вопросов)
$ perl -le 'my $a = q{abcabdabe}; print $1 while ( $a =~ /(ab.)/g );'
abc
abd
abe
Без /g этого не происходит.
/a/g
это короткая запись его конструктора.А если не хочется беспокоиться что там у нас в состоянии находится нужно просто создать новый регэксп.
вызванный несколько раз на одном и том же экземпляре глобального регулярного выражения, будет начинать проверку с конца предыдущего сопоставления.
Обычно, чтобы не следить за этим, используют методы строки, куда передается регэксп, т.е. String#match и String#search.
Заметьте, что в данном примере мы как раз вызываем методы самого объекта регэксп.
Любой парсинг требует возможность продолжения с точки последней остановки.
В моём понимании регэксп это детерминированный тест, который всегда возвращает одно и то же,
'somestring'.match(/someregexp/)
Вот так вот используют RegExp-ы в JS практически всегда. И да, оно работает именно так, как вы описали. А то что в статье, это… ну такой более низкоуровневый примитив для хитрых нужд.
Вы можете тут спросить: «Зависит ли результат от порядка следования байтов в системе, в которой выполняется код? А если так — почему я пользуюсь архитектурой, где сначала идёт старший байт?». А я на это отвечу: «Спасибо, что спросили. Хороший вопрос».
Простите, но я не увидел ответ на вопрос. Логично, что если Вы совершаете манипуляции с числами в рамках одной системы — проблем с Endian не будет. Но если это массив данных с другого устройства, где архитектура работает на BigEndian, то у Вас очевидно будут проблемы. Дело в том, что Вы не всегда определяете архитектуру системы. Например, MIPS архитектура полностью построена на BE и является практически стандартом на российском рынке импортозамещения. На мой взгляд, Вы не закончили мысль.
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
АХАХХАХАХАХА.
Что заставляет людей настолько прирастать душой к инструментам, делая из них субкультуру? Боязнь разочарования в собственном выборе?
Чушь. Ничего не исправили они. Проблем с этим не было изначально ибо был unicode(). Просто кто-то сказал "давайте будем прогрессивными" и сделали utf8 дефолтом. Даже не думая, что во всех других системных языках utf отдельно идёт (что правильно ибо подсистемы все (сеть, диски, память) работают в C локале.).
В общем разом убили простоту питона в угоду новомодью. При этом проблемы с тредингом до сих пор не решают чтобы "не ломать компатабилити". Где логика? В моем компе 8 ядер, ему через год 10 лет будет! Это уже не смешно.
GIL — проблема не в стандарте языка а конкретной реализации, хоть и основной. Делать thread safe решения ударило по производительности однопоточной версии. Неприятно конечно, но тут не причина в совместимости, разве что возможно частично на уровне C API.
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.
Строго говоря это «с++ фреймворк». Можно и консольные тулзы писать )
> с таким же читабельным синтаксисом
На вкус и цвет, а по мне
",".join(arr)
дичь полнейшая.Почему не
arr.join(",")
?Ну и прочее подобное (хорошо запомнилось только это).
Golang в разы читабельней и меньше «правил чтения» надо запоминать
Golang в разы читабельнейЭто скорее маргинальное мнение (субъективность читаемости оставляет мало места метрикам). По-моему, Python хвалят за читаемость, а Go критикуют за многословность из-за бедного синтаксиса, то есть наоборот. Обилие синтаксиса не особо коррелирует с читаемостью, чаще наоборот. Brainfuck как раз основан на минимализме правил. Из менее радикальных примеров, в Lisp минимум правил и в Forth без надстроек. Тоже часто критикуемы за нечитаемость.
Почему не arr.join(",")Выбор явно намеренный, видимо, автору это показалось более похоже на естественный английский. Читайте как "," joins array. Это, можно сказать, идеология Python. Могу согласиться, что это сомнительная цель и сомнительное решение с т.з. Computer Science.
string.join
принимает аргументом любой iterable.Если бы делали наоборот, то пришлось бы определять методы
join
у списков, кортежей, множеств, генераторов и чего угодно ещё.Обилие синтаксиса не особо коррелирует с читаемостью, чаще наоборот. Brainfuck как раз основан на минимализме правил. Из менее радикальных примеров, в Lisp минимум правил и в Forth без надстроек. Тоже часто критикуемы за нечитаемость.Языки с избыточно богатым синтаксисом, например Perl, так же часто критикуемы за нечитаемость.
Даже не думая, что во всех других системных языках 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
2020 год, люди до сих пор не отличают язык программирования отличают от рантайма
А уж блок где автор ругается на (x, y, z) == (...) вообще вызвал смех. Как можно жаловаться на скобочки в языке, основанном на лиспе?
Но ведь это выглядит как список/кортеж, а не progn.
Лично для меня список выглядит с квадратными скобками, а с круглыми — это выглядит как перечисление. Перечисляли выражения через запятую, в конце сравнили результаты перечисления.
Всё субъективно…
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.
Чем же вам так насолило прототипное программирование?
Дык я и говорю про то, что на го удобно писать мелкие программы
Что по вашему "не мелко" — что то с GUI? Тут да, go сосет.
Чем же вам так насолило прототипное программирование?
Полной не логичностью. Еще скажите что сахар типа class просто так ввели )
ЗЫ. Докер, кубер — это все мелко ?
"Не мелко" — либо объем кода либо кол-во интфейсов/классов. Вот у меня есть проект на TS, где 50+ классов/интерфейсов, строк кода несколько тысяч или больше (сомневаюсь, что больше 10к, я не веду учет), и я не представляю как это бы все смотрелось на Го. Скорее всего на го кода было бы в полтора раза больше.
Насчет прототипного программирования вы говорите слишком абстрактно, возможно, вы в этом не разбирались
"Не мелко" — либо объем кода либо кол-во интфейсов/классов
Ну вот есть докер с кубером. Понимаю что там может быть написано все "вопреки", но сомневаюсь.
Насчет прототипного программирования вы говорите слишком абстрактно, возможно, вы в этом не разбирались
Именно! Я не считаю нужным разбираться в мутных вещах )
А тем более использовать их.
ЗЫ. String.prototype.OLOLO и ололо? )
Мдя, без желания узнать как и что устроено хорошим разработчиком трудно стать.
А вы видели исходники докера/кубера? Или это опять просто популистические высказывания?
Мдя, без желания узнать как и что устроено хорошим разработчиком трудно стать.
Извиняюсь но не в случае с js. Там я знаю то что мне нужно (не используй колбеки, опасайся for...in и тому подобное. Не используй наследование :)).
А вы видели исходники докера/кубера?
Нет, но давайте с вами разберем, если хотите. Просто не считаю это "мелкой" программой вот и все.
А если вы про НАЛИЧИЕ исходников — все на гитхабе есть.
Извиняюсь но не в случае с js. Там я знаю то что мне нужно (не используй колбеки, опасайся for...in и тому подобное. Не используй наследование :)).
Эту мысль можно было бы развернуть, но я не буду )
А вы знали, что суффикс "сь" указывает на говорящего? =) Т.е. вы извиняете себя =)
Что-то думать (не считать "мелкой" программой) и приводить в качестве аргумента — не одно и то же
А вы знали, что суффикс "сь" указывает на говорящего? =) Т.е. вы извиняете себя =)
А вы знали что обычно до букв докапываются те, кому больше нечего сказать ?
Что-то думать (не считать "мелкой" программой) и приводить в качестве аргумента — не одно и то же
Тут вообще какая то дич не понятная. Что хотели донести? Что я Хуй ?
Насчёт "сь" — запоминающееся событие в школе. Поэтому обратил внимание =)
Но на самом деле это ещё раз иллюстрирует то, что вы не разбираетесь в тонкостях того инструмента, которым пользуетесь =).
Друже, в "тонкостях" я разбираюсь. И эти тонкости мне не всегда нра. О том и речь. А ты походу выпил уже (как и я :))
Если обидел, извини.
Но меня часто выбешивает использование чего-либо в популистическом смысле (когда не разобрался, а говоришь лишь бы украсить высказывание).
Не, у меня 5 час утра
Если обидел, извини.
Обычно меня можно обидеть, если сказать что я в чем то не шарю ) Ну впринципе ты так и сказал, но я не зол )
Я никогда себе не позволяю что то сказать о том в чем не разбираюсь )
С учётом того, что в мире много всего, чего вы можете не знать, обидеть вас довольно легко =)
А вообще в таких случаях действует простое правило: "Не стыдно не знать, стыдно не учиться". По крайней мере я так говорю своим знакомым и на них это работает =)
Триггер у меня сработал на ваше высказывание про докер, т.к. часто это вижу, и вижу что люди не разбираются в этом. В том, какие трудности были, в том как это работает изнутри.
С учётом того, что в мире много всего, чего вы можете не знать, обидеть вас довольно легко =)
Не правильно высказался. Я имел ввиду "кто то скажет что я не прав в своем высказывании", а я обычно знаю о чем говорю ) Но не стыжусь признавать ошибки конечно )
В том, какие трудности были, в том как это работает изнутри.
Мне кажется это не было связано с языком. Но могу ошибаться конечно )
А методы можно заменить процедурами.
Из-за меток. Да в 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 на мой скромный взгляд, — это уже своего рода «ассемблер» на фоне целой кучи языков и диалектов со статическим анализом и вот этим вот всем, которые в него транслируются, если это нужно.
Несколько лет использовал Node.js. В последнее время выбираю альтернативы: Python, Erlang (Go как-то не по душе). В том числе и по тем же причинам, что о
Я перехожу на JavaScript