Пока вы не примите как факт то, что 99% программистов (если не 99.99%) наплевать сейчас и всегда будет наплевать на математическую красоту вашего кода и не придумаете как, тем не менее, использовать их энергию «в мирных целях»… ваш язык так и останется «детским садом».
Так я ведь и не заинтересован чтобы те, которым "плевать", приходили в Haskell коммьюнити. Напротив, я рад, что их там нет, и это один из аргументов в пользу моего личного выбора. Я хочу чтобы в этом коммьюнити были только стоящие представители. И поменьше закомплексованных детей, коих достаточно как в сообществе JavaScript, так и упомянутого Rust.
Ok. Что там с геренарацией C++? Смотрим, ищем… поддержка C99 есть, C++ нет. Вы эта… кто рассказывал про то, что Haskell отлично генераторы года пишутся?
Так пишутся отлично, только просто не нашлось того, кто написал.
Всё есть — и ничего нету. Потому что людей, которые «понимают значение языка и выгоду от него» категорически недостаточно для того, чтобы создать достаточно обширную базу, чтобы на ней можно было строить что-то сложнее игрушечных примеров.
Ну вот вы сами и ответили, почему дело обстоит так. И если вы спросите меня, то я для себя в целом условно "разочаровался" в перспективах человечества создания/эволюции и использования действительно хороших инструментов для программирования. Это всегда будет меньшинство, если и те не обмельчают тоже. Слишком мало умных и талантливых людей. Но лично меня в Haskell сообществе и привлекает, что туда попадают и остаются там только талантливые люди, которые чего-то стоят. А не так, что вот есть не самый плохой TypeScript, но никакого желания прикасаться к чужому коду и близко нет, т.к. порог такой низкий, что когда приходишь рандомный в проект, постояно подскальзываешься на детских обосраных памперсах.
Вы записали самое важное, самое ценное, самое главное достижение языка (как у TypeScript, так и Rust, кстати)… как его недостаток.
У нас разные представления о том, что считать "достижениями". На кой мне участвовать в проекте, со сколь угодно распрекрасным инструментом, если по факту мне приходится иметь дело с самым обыкновенным среднестатистическим fragile JavaScript shitcode-ом?
TypeScript использованный не по назначению (к чему моя основная претензия аппелирует) — это не более чем time waster. Типизирование кода в TypeScript жрёт много времени, а если у вас повсюду расставлены as any и выключен strictNullChecks, то это лишнее время отправленное в мусорку. Мой поинт прозрачен. Бесполезные инструменты должны быть элиминированы.
О да. Это — вообще чудесная вещь. Я когда писал про «вы всё время перестраиваете фундамент у вашего дома и всё ломаете?» — имел в виду именно это.
Но ведь это именно что и не так. Это стандартная/дефолтная библиотека, это не фундамент языка, а набор частоупотребимых функций. Который как раз-таки очень стабилен, с тех самых времён, и лично мне этот "багаж" не нравится. По-этому я стараюсь избегать стандартную библиотеку в пользу альтернатив. Как раз потому, что обратную совместимость решили сохранить, как вы любите, в проигрыш стройности. Мимо.
Не подскажите когда в стандарт завезли вот эту вот конструкцию: [i|Хотите пригласить человека #{name}?|]? Заметьте: это специально без выпендрёжа код!
О, ну если вы исключительно о стандарте, то я не сторонник того, чтобы ограничиваться исключительно стандартом. Я регулярно использую обилие расширений GHC, без них жизнь была бы гораздо менее приятной. Я всегда ассоциирую Haskell со всем этим богатством расширений GHC. Конечно, это не часть стандарта (последний из которых датирован 2010 годом).
А вот Perl из-за подобной попытки просто… кончился. Ну это, просто, чтобы вы понимали — как часто можно ломать совместимость и в камих масштабах.
Он не кончился. Perl 6 просто решили переименовать в Raku, чтобы исключить конфуз, т.к. там не поломка обратной совместимости, а просто новый язык, на который оказал сильное влияние Perl 5 (и Haskell, как ни странно, на котором была написана первая прототипная версия интерпретатора Perl 6).
Зависимости там ломали, если конечно чего-то не знаю, отнюдь не часто, а напротив, Perl 6 — это исключительное событие за всю историю.
Обратная совместимость там просто не предполагалась как явление.
Лично мне Raku, как язык, симпатичен, но его жирнющая VM (эталонная реализация), которая есть раму как не в себя, меня отторгает от написания чего-то, кроме небольших скриптов под задачи "быстро отработал и завершился".
Ох, Rust меня сильно разочаровал своим подходом, я возлагал на него надежды, пока не попробовал поконтрибьютить в проекты написанные на нём.
Предлагаю почитать мой тред по поводу засилья fragile .unwrap()-ов: https://mastodon.social/@unclechu/103998022358094226
Вот я рядом радовался, что из Monad в Haskell выпилили fail (теоретически где-то сломав обратную совместимость), а в Rust его применение впилили в один из самых широкоиспользуемых методов. Новый язык не должен таким образом подходить к проблемам. Это принципиально ничем не лучше, чем NullPointerException.
А вопрос участника треда:
unclechu But how do you express in the type system that Regex::new("[a-z]") can't fail?
Меня просто убил (я как его прочитал сразу прикинул реализацию на Haskell в голове на темплейтах и парсерах), и окончательно убедил, что сообщество Rust, которые повсюду пишут .unwrap() действительно технически не очень подтянуто. И сам язык, экосистема, как следствие, это отражает.
Так что нет, отсылка к Rust для меня не пример чего-то well-done.
Чтобы разрешить ошибки наследия прошлого. В стократ более безобидные, и вполне себе исправимые в конечном коде. У вас есть статическая типизация, которая укажет вам что не так, вы их не пропустите. Это же идеальный сценарий, нормально развивающегося языка. А те или иные ошибки неизбежны везде, где-то только их тащат до самой смерти языка, когда багаж уже так велик, что язык становится неподъёмным для освоения, и язык умирает. А где-то происходит контролируемая эволюция.
В том же Monad не так давно были изменения. Из определения Monad вынесли определение fail (который математически не является частью Monad) в отдельный тайпкласс MonadFail. Точнее происходило это поэтапно, сначала сделали MonadFail, задепрекейтили Monad.fail предлагая использовать MonadFail, дав время начать его использовать. А потом удалили из определения Monad.
И лично меня это изменение не может не радовать, язык стал более стройный, более правильный и безопасный (т.к. зависимость от Monad в полиморфной функции больше не предполагает имплицитно возможность падения с ошибкой), т.е. улучшился контроль эффектов. Если бы fail остался в Monad я бы меньше любил Haskell, мне всегда это не нравилось.
А если где-то что-то "сломалось", то комплиятор вам заботливо об этом сообщит, потому что сильная статическая типизация. И даже предложит просто заимпортировать Control.Monad.Fail в качестве решения. По-моему это то, как должен развиваться хороший язык.
А теперь давайте вернёмся к JavaScript (и UB), о котором речь шла изначально, а мы зачем-то перешли к обсуждению к Haskell. Помним правило ассоциативности же? Ну со школы же знаем что (a + b) + c всё-равно что a + (b + c), но не когда мы говорим о JavaScript.
Вот это я понимаю дизайн и соответствие стандарту. Понятно, что данный пример в вакууме — это не реальный юзкейс. Но что из себя представляет стандарт JavaScript, вопрос о его "стройности" и стабильности, — вполне себе показывает. Я честно говоря и не в курсе, UB это, или кто-то из браузерных движков фундаментально не прав. Боюсь проверить данный пример в более старых реализациях. Можно посмотреть на числа:
Понятно что это погрешности при работе с плавающей точкой. Но JavaScript не проводит никакой типовой разницы между целочисленными и дробными. Так что можно смело утверждать, что правило ассоциативности не выполняется и для чисел.
Проблема не в том, что он чего-то кому-то там подсказал. Проблема в том, что он это подсказал на код из книжки! Из тьюториала на haskell.org. Причём там ни один из тьюториалов не работает!
Я не знаю как давно этот код работал, возможно когда-то у Monadиз стандартной библиотеки в зависимостях не было Applicative.
Лично я не считаю что обратной совместимости нужно поклоняться и расписываться кровью в её сохранении. Тем более не с языком-однодневкой, а языком, который пережил многих (Haskell не сильно младше Си) и до сих пор считается крутым, аналогов которому почти нет. Тем не менее, я согласен что нарушать обратную совместимость слишком часто не стоит, или существенно её ломать. Но иногда без таких изменений кина не будет. Можно посмотреть на Си++, изучив все (ну или большинство) подводные грабли и прочие UB-кейсы которого можно лет спустя 10 каждодневной практики (тяжёлая ноша "обратной совместимости"). Тем не менее в Haskell прямо таких фундаментальных "поломок" было за историю на моей памяти очень мало. И это были не сколько "поломки", сколько тюнинг зависимостей тайпклассов и тому подобное.
Это раз. А два — реализация тайпкласса Monad — это не непосредственная неотъемлемая часть реализации языка в GHC, это часть стандартной библиотеки base, которая по большей части живёт на правах любой другой библиотеки. Есть даже альтернативные реализации "стандартной библиотеки" также улучшающие ситуацию с многолетним наследием той же "обратной совместимости" вроде кучки нетотальных или необоснованно мономорфных функций.
А насчёт "ни один из туториалов не работает" я уверен что вы преувеличиваете. Я сам учился по этой книге сколько-то лет назад, правда дочитал где-то до середины, потом отвлёкся на практические задачи в плане реализации собственных идей. А когда обнаружил себя копающимся в type-level программировании понял что эта книга уже ничего нового для меня не откроет, т.к. я её уже перепрыгнул. Тем не менее, первичный старт она мне дала очень хороший, и у меня в целом осталось очень положительное впечатление о книге.
Я не могу сделать тоже самое с Haskell. У него вообще нет ни одного компилятора совместимого со стандартом.
Прошу пример конкретного несоответствия со стандартом, которое вам досаждает? И с каким Haskell2010? Haskell98?
Прелестно? А я, между прочим, новичёк, я только начал книжку читать, ещё даже до Hello, World не добрался, возможно (взависимости от того, как книжка структурирована).
Причём тут вообще сложность изучения Haskell, когда мы говорим о дет-садовском JavaScript? Я пример на Haskell привёл по двум причинам, чтобы прежде всего показать что я имею в виду, если из описания не очевидно или сложно понять (у человека не посвящённого в аппликативные функторы сама задача может плохо укладываться в голове из-за отсутствующей модели осмысления проблемы, т.к. undefined is not a function считается повседневной нормой, а не фундаментальным дефектом инструмента). А на Haskell потому, что на нём такое возможно реализовать стандартными средствами, ради наглядности. Я вам Haskell тут не пытаюсь предложить, это вы сами для себя решите, когда перерастёте JavaScript. Нечто похожее можно было бы реализовать на TypeScript, через overloads с дженериками, но путём введения кастомной функции, релизация которой была громоздкой, это было бы не интуитивно и не наглядно, понятно только опытным.
P.S. Вам компилятор подсказал что добавить в констрейнты.
просто не видел ни одного человека, который бы все эти «так не надо делать» всегда и безусловно соблюлал бы.
В мире JavaScript это прежде всего обусловлено недостатками языка, т.к. соблюдать хороший тон, пиша (не помню как правильно склонить на русском слово "писать") на нём — задача очень сложная и плохо вписывающаяся в "дизайн" языка (если слово "дизайн" тут вообще уместно употреблять).
Такое лучше делать в TypeScript через overloads, а вот так вот, как в этом примере, обрабатывать аргументы — это что, не костыль по вашему? Особенно когда аргументы называются не xy, а там options и callback.
Но вообще я не сторонник таких подходов, т.к. они fragile в плане возможных опечаток, забытых аргументов и тому подобное. Целесообразнее на мой взгляд использовать отдельные функции, или явно передать null/Nothing/whatever.
Лично у меня в таком (в применении overloads) необходимость возникала только при эмуляции тех же аппликативных функторов и банальной point-free композиции функций на TypeScript. Т.е. чтобы реализовать подобие отсутствующих в языке конструкций.
Не совсем, это слишком простой сценарий. Есть допустим функция с парой аргументов, оригинальная функция не умеет работать с null/undefined. Есть пара значений для аргументов каждый из которых можеть быть null. Нужно вызвать эту функцию, не изменяя оригинальную функцию. Если любой из аргументов равен null, т.е. функция не должна вызываться и всё выражение результируется в null/undefined.
В качестве примера кусочек кода на Haskell:
-- Описание типа (Maybe a) - это всё-равно что "nullable"
-- где (Just a) - это существующее значение любого типа "a",
-- а Nothing - можно воспринимать как null.
data Maybe a = Just a | Nothing
add :: Integer -> Integer -> Integer
add a b = a + b
x :: Maybe Integer
x = Just 10
y :: Maybe Integer
y = Just 20
sum = add <$> x <*> y -- Just 30
sum2 = add <$> Nothing <*> y -- Nothing
sum3 = add <$> x <*> Nothing -- Nothing
sum4 = add <$> Nothing <*> Nothing -- Nothing
-- Также сама функция может быть
-- (Maybe (Integer -> Integer -> Integer))
sum5 = Just add <*> x <*> y -- Just 30
sum6 = Nothing <*> x <*> y -- Nothing
Можно я к вам в детский садик загляну с вселенским откровением?
C#, Kotlin, теперь вот, прости Дарвин, JavaScript, что там ещё. Вот этот вот elvis-operator — это вы (ну или вам, в этих ваших языках, доступных для масс) просто монады переизобрели. Только переизобрели из рук вон плохо, не композируемо, не полиморфно, однобоко, к вызову функций вон уже не применишь, и никаких вам аппликативных функторов, т.к. маленькие ещё в такое играть.
IPv6 — это хорошо, в европах используется, извлекается профит, но пока VPN-сервисы не предлагают поддержку анонимайзинга через IPv6, а с учётом уже сегодня полностью сломанного internet neutrality, страшного засилья копирастии, цензуры и карательных процедур т.н. "интернет-преступлений" — это очень большой довод в пользу сохранения status quo IPv4.
У меня стоит пол сотни плагинов, Vim запускается почти мгновенно (пол сотни, которые расширяют функционал, более сотни, включая поддержку многих языков и форматов файлов и цветовые схемы).
Остальное я даже комментировать не буду, глупости одни.
Так я ведь и не заинтересован чтобы те, которым "плевать", приходили в Haskell коммьюнити. Напротив, я рад, что их там нет, и это один из аргументов в пользу моего личного выбора. Я хочу чтобы в этом коммьюнити были только стоящие представители. И поменьше закомплексованных детей, коих достаточно как в сообществе JavaScript, так и упомянутого Rust.
Так пишутся отлично, только просто не нашлось того, кто написал.
Ну вот вы сами и ответили, почему дело обстоит так. И если вы спросите меня, то я для себя в целом условно "разочаровался" в перспективах человечества создания/эволюции и использования действительно хороших инструментов для программирования. Это всегда будет меньшинство, если и те не обмельчают тоже. Слишком мало умных и талантливых людей. Но лично меня в Haskell сообществе и привлекает, что туда попадают и остаются там только талантливые люди, которые чего-то стоят. А не так, что вот есть не самый плохой TypeScript, но никакого желания прикасаться к чужому коду и близко нет, т.к. порог такой низкий, что когда приходишь рандомный в проект, постояно подскальзываешься на детских обосраных памперсах.
У нас разные представления о том, что считать "достижениями". На кой мне участвовать в проекте, со сколь угодно распрекрасным инструментом, если по факту мне приходится иметь дело с самым обыкновенным среднестатистическим fragile JavaScript shitcode-ом?
TypeScript использованный не по назначению (к чему моя основная претензия аппелирует) — это не более чем time waster. Типизирование кода в TypeScript жрёт много времени, а если у вас повсюду расставлены
as anyи выключенstrictNullChecks, то это лишнее время отправленное в мусорку. Мой поинт прозрачен. Бесполезные инструменты должны быть элиминированы.Но ведь это именно что и не так. Это стандартная/дефолтная библиотека, это не фундамент языка, а набор частоупотребимых функций. Который как раз-таки очень стабилен, с тех самых времён, и лично мне этот "багаж" не нравится. По-этому я стараюсь избегать стандартную библиотеку в пользу альтернатив. Как раз потому, что обратную совместимость решили сохранить, как вы любите, в проигрыш стройности. Мимо.
О, ну если вы исключительно о стандарте, то я не сторонник того, чтобы ограничиваться исключительно стандартом. Я регулярно использую обилие расширений GHC, без них жизнь была бы гораздо менее приятной. Я всегда ассоциирую Haskell со всем этим богатством расширений GHC. Конечно, это не часть стандарта (последний из которых датирован 2010 годом).
Он не кончился. Perl 6 просто решили переименовать в Raku, чтобы исключить конфуз, т.к. там не поломка обратной совместимости, а просто новый язык, на который оказал сильное влияние Perl 5 (и Haskell, как ни странно, на котором была написана первая прототипная версия интерпретатора Perl 6).
Зависимости там ломали, если конечно чего-то не знаю, отнюдь не часто, а напротив, Perl 6 — это исключительное событие за всю историю.
Обратная совместимость там просто не предполагалась как явление.
Лично мне Raku, как язык, симпатичен, но его жирнющая VM (эталонная реализация), которая есть раму как не в себя, меня отторгает от написания чего-то, кроме небольших скриптов под задачи "быстро отработал и завершился".
Ох, Rust меня сильно разочаровал своим подходом, я возлагал на него надежды, пока не попробовал поконтрибьютить в проекты написанные на нём.
Предлагаю почитать мой тред по поводу засилья fragile
.unwrap()-ов:https://mastodon.social/@unclechu/103998022358094226
Вот я рядом радовался, что из
Monadв Haskell выпилилиfail(теоретически где-то сломав обратную совместимость), а в Rust его применение впилили в один из самых широкоиспользуемых методов. Новый язык не должен таким образом подходить к проблемам. Это принципиально ничем не лучше, чемNullPointerException.А вопрос участника треда:
Меня просто убил (я как его прочитал сразу прикинул реализацию на Haskell в голове на темплейтах и парсерах), и окончательно убедил, что сообщество Rust, которые повсюду пишут
.unwrap()действительно технически не очень подтянуто. И сам язык, экосистема, как следствие, это отражает.Так что нет, отсылка к Rust для меня не пример чего-то well-done.
Чтобы разрешить ошибки наследия прошлого. В стократ более безобидные, и вполне себе исправимые в конечном коде. У вас есть статическая типизация, которая укажет вам что не так, вы их не пропустите. Это же идеальный сценарий, нормально развивающегося языка. А те или иные ошибки неизбежны везде, где-то только их тащат до самой смерти языка, когда багаж уже так велик, что язык становится неподъёмным для освоения, и язык умирает. А где-то происходит контролируемая эволюция.
В том же
Monadне так давно были изменения. Из определенияMonadвынесли определениеfail(который математически не является частьюMonad) в отдельный тайпклассMonadFail. Точнее происходило это поэтапно, сначала сделалиMonadFail, задепрекейтилиMonad.failпредлагая использоватьMonadFail, дав время начать его использовать. А потом удалили из определенияMonad.И лично меня это изменение не может не радовать, язык стал более стройный, более правильный и безопасный (т.к. зависимость от
Monadв полиморфной функции больше не предполагает имплицитно возможность падения с ошибкой), т.е. улучшился контроль эффектов. Если быfailостался вMonadя бы меньше любил Haskell, мне всегда это не нравилось.А если где-то что-то "сломалось", то комплиятор вам заботливо об этом сообщит, потому что сильная статическая типизация. И даже предложит просто заимпортировать
Control.Monad.Failв качестве решения. По-моему это то, как должен развиваться хороший язык.А теперь давайте вернёмся к JavaScript (и UB), о котором речь шла изначально, а мы зачем-то перешли к обсуждению к Haskell. Помним правило ассоциативности же? Ну со школы же знаем что
(a + b) + cвсё-равно чтоa + (b + c), но не когда мы говорим о JavaScript.А главное, что JavaScript — платформонезависимый! Пример выше — Firefox. Ниже Chromium:
Вот это я понимаю дизайн и соответствие стандарту. Понятно, что данный пример в вакууме — это не реальный юзкейс. Но что из себя представляет стандарт JavaScript, вопрос о его "стройности" и стабильности, — вполне себе показывает. Я честно говоря и не в курсе, UB это, или кто-то из браузерных движков фундаментально не прав. Боюсь проверить данный пример в более старых реализациях. Можно посмотреть на числа:
Понятно что это погрешности при работе с плавающей точкой. Но JavaScript не проводит никакой типовой разницы между целочисленными и дробными. Так что можно смело утверждать, что правило ассоциативности не выполняется и для чисел.
Я не знаю как давно этот код работал, возможно когда-то у
Monadиз стандартной библиотеки в зависимостях не былоApplicative.Лично я не считаю что обратной совместимости нужно поклоняться и расписываться кровью в её сохранении. Тем более не с языком-однодневкой, а языком, который пережил многих (Haskell не сильно младше Си) и до сих пор считается крутым, аналогов которому почти нет. Тем не менее, я согласен что нарушать обратную совместимость слишком часто не стоит, или существенно её ломать. Но иногда без таких изменений кина не будет. Можно посмотреть на Си++, изучив все (ну или большинство) подводные грабли и прочие UB-кейсы которого можно лет спустя 10 каждодневной практики (тяжёлая ноша "обратной совместимости"). Тем не менее в Haskell прямо таких фундаментальных "поломок" было за историю на моей памяти очень мало. И это были не сколько "поломки", сколько тюнинг зависимостей тайпклассов и тому подобное.
Это раз. А два — реализация тайпкласса
Monad— это не непосредственная неотъемлемая часть реализации языка в GHC, это часть стандартной библиотекиbase, которая по большей части живёт на правах любой другой библиотеки. Есть даже альтернативные реализации "стандартной библиотеки" также улучшающие ситуацию с многолетним наследием той же "обратной совместимости" вроде кучки нетотальных или необоснованно мономорфных функций.А насчёт "ни один из туториалов не работает" я уверен что вы преувеличиваете. Я сам учился по этой книге сколько-то лет назад, правда дочитал где-то до середины, потом отвлёкся на практические задачи в плане реализации собственных идей. А когда обнаружил себя копающимся в type-level программировании понял что эта книга уже ничего нового для меня не откроет, т.к. я её уже перепрыгнул. Тем не менее, первичный старт она мне дала очень хороший, и у меня в целом осталось очень положительное впечатление о книге.
Прошу пример конкретного несоответствия со стандартом, которое вам досаждает? И с каким Haskell2010? Haskell98?
Можно конкретнее, о чём идёт речь?
От чего же нет? Возникала, и не раз. Что это меняет?
Причём тут вообще сложность изучения Haskell, когда мы говорим о дет-садовском JavaScript? Я пример на Haskell привёл по двум причинам, чтобы прежде всего показать что я имею в виду, если из описания не очевидно или сложно понять (у человека не посвящённого в аппликативные функторы сама задача может плохо укладываться в голове из-за отсутствующей модели осмысления проблемы, т.к.
undefined is not a functionсчитается повседневной нормой, а не фундаментальным дефектом инструмента). А на Haskell потому, что на нём такое возможно реализовать стандартными средствами, ради наглядности. Я вам Haskell тут не пытаюсь предложить, это вы сами для себя решите, когда перерастёте JavaScript. Нечто похожее можно было бы реализовать на TypeScript, через overloads с дженериками, но путём введения кастомной функции, релизация которой была громоздкой, это было бы не интуитивно и не наглядно, понятно только опытным.P.S. Вам компилятор подсказал что добавить в констрейнты.
Внести изменение в реализацию, и исправлять ошибки компилятора, пока не скомпилируется, ничего сверхъестественного.
Но также можно и написать альтернативную функцию, а запатченый оригинал вызывать как враппер над ней.
В мире JavaScript это прежде всего обусловлено недостатками языка, т.к. соблюдать хороший тон, пиша (не помню как правильно склонить на русском слово "писать") на нём — задача очень сложная и плохо вписывающаяся в "дизайн" языка (если слово "дизайн" тут вообще уместно употреблять).
Такое лучше делать в TypeScript через overloads, а вот так вот, как в этом примере, обрабатывать аргументы — это что, не костыль по вашему? Особенно когда аргументы называются не
xy, а тамoptionsиcallback.Но вообще я не сторонник таких подходов, т.к. они fragile в плане возможных опечаток, забытых аргументов и тому подобное. Целесообразнее на мой взгляд использовать отдельные функции, или явно передать
null/Nothing/whatever.Лично у меня в таком (в применении overloads) необходимость возникала только при эмуляции тех же аппликативных функторов и банальной point-free композиции функций на TypeScript. Т.е. чтобы реализовать подобие отсутствующих в языке конструкций.
Не совсем, это слишком простой сценарий. Есть допустим функция с парой аргументов, оригинальная функция не умеет работать с
null/undefined. Есть пара значений для аргументов каждый из которых можеть бытьnull. Нужно вызвать эту функцию, не изменяя оригинальную функцию. Если любой из аргументов равенnull, т.е. функция не должна вызываться и всё выражение результируется вnull/undefined.В качестве примера кусочек кода на Haskell:
Можно я к вам в детский садик загляну с вселенским откровением?
C#, Kotlin, теперь вот, прости Дарвин, JavaScript, что там ещё. Вот этот вот elvis-operator — это вы (ну или вам, в этих ваших языках, доступных для масс) просто монады переизобрели. Только переизобрели из рук вон плохо, не композируемо, не полиморфно, однобоко, к вызову функций вон уже не применишь, и никаких вам аппликативных функторов, т.к. маленькие ещё в такое играть.
Лучше поинтересоваться откуда в C# это взяли.
IPv6 — это хорошо, в европах используется, извлекается профит, но пока VPN-сервисы не предлагают поддержку анонимайзинга через IPv6, а с учётом уже сегодня полностью сломанного internet neutrality, страшного засилья копирастии, цензуры и карательных процедур т.н. "интернет-преступлений" — это очень большой довод в пользу сохранения status quo IPv4.
HTML, CSS & JavaScript?
У меня стоит пол сотни плагинов, Vim запускается почти мгновенно (пол сотни, которые расширяют функционал, более сотни, включая поддержку многих языков и форматов файлов и цветовые схемы).
Остальное я даже комментировать не буду, глупости одни.