Приверженцы статической и динамической типизаций никогда не поймут друг друга. И TypeScript им не поможет



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

    Я начал учить плюсы и C#, он — JavaScript. Закончили школу, отучились в универах, отслужили в армии, устроились на работы. Нас потаскало промышленной разработкой там и тут, и когда мы оба от нее подустали, вспомнили с чего начинали.

    Собравшись уже матерыми разрабами, мы решили, наконец, сделать свой проект — двумерную видеоигру. Так как друг был чистый фронт, а я фулстек, очевидной платформой для нас стал браузер. Раньше я разрабатывал фронт только на TypeScript, и мы подумали, никаких проблем, TS — всего-лишь надмножество JavaScript. Возьмем его и все пройдет гладко. Как бы не так. Когда мы начали обсуждать работу, столкнулись с невероятной бездной непонимания друг друга.

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

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

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

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

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

    У статической и динамической типизации есть принципиальная непримиримая разница


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

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

    Идея ставить во главу типы серьезно повлияла на мое мышление разработчика.
    Выбрав в начале пути C#, я прибил гвоздями статическую типизацию к своему мировоззрению, от чего теперь и страдаю. Увидев любую задачу, я пытаюсь представить ее решение как набор типов и принципов их взаимодействия. Когда я разрабатываю модуль, первым делом определяю, какими типами он оперирует и какими взаимодействует со своим окружением. Я даже не помню, как я подходил к решению задач раньше.

    Все обучение программированию на Java — это обучение проектированию и использованию типов. .NET CLR — рантайм C# — построен на типах и для типов. Cтатическая типизация лежит в центре дизайна объектно-ориентированного программирования (привет, классы из JS, я решил, что вы не нужны). Канонические реализации большинства ООП паттернов пестрят кейвордом Interface, который совершенно бессмысленнен в динамически типизированном языке.

    Сами паттерны — штука кросс-языковая, но кто-нибудь может мне сказать, зачем нужен паттерн «состояние» в динамически типизированном языке? А билдер? Эти паттерны не про разработку, они про типы. Типы и ООП связаны неразрывно.

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

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

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



    Эти ребята по неведомой причине просто не готовы жить в мире, где их видение содержит серьезные пробелы. Поэтому я критикую только тех из них, кто суется в мой definitely-typed проект, и наводит там свои any порядки.

    Мы бы так и жили в своих мирках, но TypeScript нас столкнул


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

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

    Самый разрушительный аргумент — TypeScript лишь надмножество JS. Но на практике ты не можешь не рассматривать Тайпскрипт, как самостоятельный язык, даже если ты чертов король фронтендеров. Потому что тут нужен другой подход — статический, а не динамический.

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

    Многим кажется, что TypeScript — это компромисс систем типов между JS и Java. Никакой он не компромисс, его система типов просто особенная.

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

    JavaScript, как мне видится, очень хороший инструмент, чтобы написать код, который решает проблему, не выделяя при этом лишние абстракции. Самый вопиющий кейс в нем — паттерн Ice factory. Этой штуке можно скармливать свои инстансы, и она обмазывает их рантайм-иммутабельностью. Если я обработаю этой фабрикой свой объект, она отдаст мне такой же, но при попытке изменить значение одного из свойств, я получу исключение. Просто, WAT?!?

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

    С другой стороны, и это не нужно, потому что есть чистое функциональное программирование. Бери F#, Haskell, OCaml, Кложу, ReasonML — там мутации запрещены из коробки. Но я боюсь, что если дать фронтендеру функциональный язык, он тут же начнет модернизировать его и сделает поведение похожим на JS.

    Потому что выбор типизации — путь без возврата. Все компромиссные решения дают только иллюзию компромисса. Ты либо ставишь во главе угла типы, либо нет. Не знаю, как бы все сложилось, начни я учить C# и JavaScript одновременно и параллельно друг другу. Но сейчас я так крепко прибит к своему мышлению, что не вижу и не хочу видеть плюсов динамической типизации. Они есть, но вне поля моего зрения, и все что мне остается — закрывать на них глаза, как на любые чуждые мне проявления мира, с которыми приходится жить. Понимаю, что не прав, но работать мне надо здесь и сейчас, и метаться между полюсами бюджета нет.

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

    Похожие публикации

    Комментарии 1199
      +11
      Типы и ООП связаны неразрывно.

      А чем, например, Ruby не ООПшный?
        0
        А куда за тот год, пока я не видел Ruby, с него убежали типы?
          +6
          Никуда не убежали, но в статические они так и не превратились.
            +20
            Мне кажется, статья будет неполной без этой, классической уже, картинки:
            static vs dynamic typing battles
            image

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

              Что касается типизации, то люблю оба типа. Динамическая очень полезна, когда надо что-то на коленке на несколько сотен строк написать.
                0
                Имеется ввиду type inference, Когда компилятор догадывается о типах самостоятельно.
                  +2

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

                    0

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

                      0
                      Как минимум, результаты статанализа можно игнорировать

                      Нельзя, если он встроен в компилятор и называется тайпчекером.

                        0

                        Речь о динамически типизируемых языках.

                        0

                        Вопрос как раз об этом, зачем нужен статанализ, результаты которого могут быть проигнорированы? Что этот статанализ вообще тогда даёт? :-)

                          0

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

              +5
              Как-то за js обидно
              +4
              Выбрав в начале пути C#, я прибил гвоздями статическую типизацию к своему мировоззрению, от чего теперь и страдаю.

              Знакомое чувство.
                0
                Ага, есть такое…
                  +7
                  Я тоже благодаря C# признаю только статическую типизацию, но не понимаю, от чего вы страдаете?
                    0
                    От того, что иногда (часто) приходится работать с ЯП, имеющими динамический подход. Вот с ними я и страдаю. Но не прям так уж чтобы сильно.

                    ЗЫ
                    А начинал я вообще с изучения Си в самом начале 90х по книге Кернигана и Ритчи, которую я, будучи студентом, случайно купил в славном городе Краснодаре.
                    Представляете, как я страдаю от самой мысли, что существуют ЯП с другими подходами? :) (шутка).
                      0
                      От лютого непонимания парадигмы F#, например. Очень трудно перестроить мозг с ООП на функциональщину. 12 лет с C# не проходят даром.
                        +2
                        Ну не надо путать теплое с мягким. Мы все же про типизацию говорим, а в F# система типов тоже строгая статическая. ООП и ФП — это из другой оперы, и, в отличие от типизации, ООП и ФП не взаимоисключающие, к нашему общему счастью. Многие недавние плюшки C# пришли после обкатки на F#.
                        +4

                        Я начинал с C#, недавно влез во фронтенд. Не пониманию страданий автора. Динамика позволяет очень легко писать костыли в самом начале, когда структура сырая и ты сам не уверен, как лучше тут сделать. Затем после нахождения определенного пути, ты просто рефакторишь это в типы и все. В итоге в проекте есть фронтир, где на каждом шагу any. А есть устаканившаяся часть, где все по-полочкам. По-моему очень удобно получается.

                      +4
                      А я начал с php и js. И теперь понимаю, насколько им мешает динамическая типизация. Точнее, мешает мне, особенно когда в чужом коде роешься.
                        +2

                        Вы уверены, что мешает вам динамическая? Большинство проблем типизации с которыми я сталкивался за почти 20 лет PHP+JS были связаны со слабой типизацией, а не динамической.

                          +3
                          Лично мне в 1с именно динамическая мешает. Когда чтобы посмотреть что нужно в метод передать приходится лезть в код этого метода. Особенно весело когда метод может не вернуть ничего, один элемент, коллекцию, или строку с ошибкой и все это одновременно.
                      +4
                      Да чё говорить-то. Хотели, как проще, а получилось как всегда. В PHP и Python, добавили аннотации типов. В PowerShell и TypeScript используются с самого начала. Тенденция, однако.
                        +8
                        Ну так оно и логично, есть выбор — мелкий проект, прототип — можно без типов, что-то серьёзное — юзай типы.
                          +2

                          Аннотации типов != Строгая/Статическая типизация. Контролируются лишь сигнатуры методов, да и то опционально и исключительно рантаймово. Считайте это автоматизацией проверок типа параметров перед началом выполнения функции/метода. Не более.

                            +1
                            На уровне языка может только и рантаймово, а вот статанализаторы очень рады. И да, в целом в PHP типизация становится строгой с включенным use strict.
                          +22
                          Намешано.
                          — я пишу на C++/go/js/js+flow/typescript, увлекаюсь Haskell и ReasonML и тп. Я понимаю какие ошибки вы описываете, но не понимаю каким боком тут JS. Писать на C# можно ещё хуже, чем на JS и типы не спасут, можно глянуть www.govnokod.ru/csharp
                          — JS относится не только к фронту, но и к беку, а вся статья пронизана ФРОНТЕНДЩИКИ ПИШУТ ОТСТОЙ
                          — в проектах совсем мало ошибок, которые уберёт статическая типизация. Никто не пишет невообразимое количество тестов на type errors в js… так же как на C# никто не пишет проверки ссылок на null повсеместно или вы пишете?
                          — много чего ещё

                          У меня вывод один, нужно расслабиться и заниматься свои делом. Новичкам эта статья не поможет, а остальные и так все понимают.
                            +6
                            Ну как пронизана, когда речь о только о методах работы с данными? Автор же так же пишет фронт, но на TypeScript, что сейчас совершенно логично делать ибо даёт все описываемые им преимущества.
                              –3
                              А вот скажите мне, как типы помогают в фронтенде? Как написать тип, когда одна переменная зависит от другой? Например бэкенд возвращает что то вроде:

                              {
                                id: 10,
                                some_flag: true,
                                other_flag: false,
                              }
                              


                              Например, если `other_flag: true`, то возвращается еще один параметр `other_param: 'Some string' `

                              {
                                id: 10,
                                some_flag: true,
                                other_flag: true,
                                other_param: 'Some string',
                              }
                              


                              Каким образом можно описать зависимость данных в типах? Только не надо писать: «Поставь `other_param?: string`». Это не решение проблемы.
                                +2

                                Вот так можно же:


                                {
                                  id: number;
                                  some_flag: boolean;
                                  other_flag: false;
                                } | {
                                  id: number;
                                  some_flag: boolean;
                                  other_flag: true;
                                  other_param: string;
                                }
                                  0
                                  И сколько нужно создавать под-типов одного типа? Данное решение является самоубийством!
                                    +4
                                    И сколько нужно создавать под-типов одного типа?

                                    Очевидно в данном случае два.


                                    Данное решение является самоубийством!

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

                                      +3

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

                                        –1
                                        Не путайте «говнокод» с извращением. То, что предлагает наш уважаемый коллега mayorovp, является извращением. Такое решение «пригодно» (очень с натяжкой можно использовать это слово) для маленьких систем, где нет больших данных. Представьте себе объект с 200 атрибутами и более 30 внутренних зависимостей. Перечислять все возможные состояния объекта (делать под-типы) будет только человек, не имеющий большого опыта в программировании и дебаге. Очень хочу посмотреть на этого человека, когда появится первый баг.

                                        Также, очень заметным является то, что наш уважаемый коллега mayorovp, имел дело только с типизацией в JavaScript. Если бы у него имелся опыт работы с типами в других языках (например функциональных или в языках с сильной типизацией), я уверен, что он бы такое решение не предложил.
                                          +8

                                          Во-первых, не надо про меня ничего выдумывать. Я имел дело и с C#, и с Haskell.


                                          Во-вторых, объект с 200 атрибутами и 30 зависимостями просто нуждается в декомпозиции. Само существование такого объекта — признак лютого говнокода. Но даже если на бэкенде сидят странные личности, которые не видят никаких проблем в таких объектах — Typescript позволяет провести декомпозицию исключительно на уровне типов с помощью упомянутого мною оператора &. Разбиваем этот мега-тип на 10 типов (именованных!) с 20 атрибутами и 3 зависимостями каждый — и вот уже все не так страшно.


                                          В-третьих, даже если вы не будете ничего разбивать, и напишите 200 опциональных свойств в одном типе — это все еще будет лучше чем то что предлагает JS.

                                            –1
                                            «Само существование такого объекта — признак лютого говнокода.»
                                            С этим не согласен. Не имея понятия, зачем это делается, не стоит сразу выносить вердикт. Но эту проблему можно смотреть с разных сторон:

                                            1) Как вы и писали — декомпозиция, т.е. подкачка данных с других источников (api endpoint). Но это несет за собой такую неприятную вешь, как отклик сервера и количество соединений с ним.
                                            2) Сделать один api endpoint, где будет вся информация.

                                            Что лучше, «засорить» сервер кучей маленьких requests или сделать один request, где будут все данные? На эту проблему нужно смотреть с высоты «нужд» и производительности системы, в частности бэкенда. То, что для вас является «говнокодом», на самом деле есть потребность.

                                            «Я имел дело и с C#, и с Haskell.»
                                            Что-то вообще, ну прям вообще не ощущается у Вас. Могу предположить, что у вас мало опыта (работы с типами) в этих языках и что основным вашим языком является JS.
                                              0

                                              То есть вариант "сделать один api endpoint, но с адекватным контрактом" вами не рассматривается в принципе?


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

                                              Могу сказать тоже самое про вас. Может быть, вы вместо пустых обвинений возьмете да расскажете как это делается на том же C#?

                                                0
                                                «Могу сказать тоже самое про вас. Может быть, вы вместо пустых обвинений возьмете да расскажете как это делается на том же C#?»

                                                Коллега, вы вероятно ошиблись. Я с С# никогда не работал и никогда этого не утверждал.

                                                «один api endpoint, но с адекватным контрактом»
                                                Поподробнее, пожалуйста.
                                                  0

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


                                                  И пример с того объекта на 200 свойств и 30 зависимостей тоже приведите.

                                                    0
                                                    А почему вы думаете, что я знаю решение? Проблема относиться к фронтенду (увы, я его писал не в Haskell) и я никогда не писал, что у меня есть решение. Я знаю о существовании проблемы, мы наступили на все грабли, на которые только могли наступить. В итоге, после несколько месяцев мучений, мы просто вырубили «flow» в этом куске кода (где работаем с этим мега-объектом), и в ручную делаем валидацию. Идеального решения этой проблемы я так и не нашел.
                                                    Поэтому я так активно принимаю участие в этой дискуссии и мне очень любопытно наблюдать за решениями, которые вы предлагаете! Разница только в том, что все эти решения мы уже пытались внедрить, но ни одно решение не дало хорошего результата (нагрузки на сервер, читаемость/поддержка/добавление кода и т.д.). В итоге мы остановились на решении, которое вы называете «говнокодом».

                                                    Очень хочется узнать о «один api endpoint, но с адекватным контрактом»
                                                      0

                                                      Вы можете сообщить хоть какие-нибудь подробности о своих затруднениях? Хоть один инвариант объекта из тех 30? Примерную структуру объекта на сервере? Правила формирования? Почему вы ожидаете что все остальные способны угадать ваши проблемы?




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


                                                      type RawData = {
                                                        id: number;
                                                        some_flag: boolean;
                                                        other_flag: boolean;
                                                        other_param?: string;
                                                      }
                                                      function hasOtherParam(x: RawData) : x is RawData & { 
                                                        other_param: string;
                                                      }
                                                      {
                                                          return x.other_flag;
                                                      }
                                                        0
                                                        По своей сути — это композиция типов и это означает, что новый тип не определен для других функций. В целях переиспользования кода (и композиций функций), все типы должны быть определены перед использованием. А это в свою очередь ведет к «types hell» — миллиону вариаций (типов) одно и того же объекта, и разобраться с этим человеку — почти не реально (что в свою очередь никаким образом машине не мешает, только пару байт сверху).

                                                        Спустя некоторое время, я все более склоняюсь к идее, что можно «вертеть типами» во все стороны, чтобы правильно реализовать задачу, но есть задачи, которые невозможно типизировать способом, который удобен программисту. DRY (do not repeat yourself), читаемый код, поддерживаемый код — это пожалуй самые уязвимые места этой проблемы.
                                                          0
                                                          А это в свою очередь ведет к «types hell» — миллиону вариаций (типов) одно и того же объекта

                                                          Вы можете написать типы с T | undefined (aka T?), тогда тип будет один.


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


                                                          Мне кажется, что все ваши упомянутые выше "грабли" были исключительно из-за того, что у вас не было человека, который бы нормально умел в используемый инструмент (flow в вашем случае). Еще вариант — дело было давно, когда во flow еще не было discriminated unions.

                                    +1
                                    Только не надо писать: «Поставь other_param?: string». Это не решение проблемы.

                                    Почему не решение?

                                      0
                                      Это является «кривым» решением в JS. Мой вопрос касался типов в общем. Не стоит использовать реализацию типов в JS как нечто стандартное, универсальное. Такое явно не прокатит в Хаскелле и вероятно в других языках тоже. Перестаньте смотреть на JS как на «Cвятой грааль», эталон.
                                        +1
                                        Это вообще не JS, это TS. Не надо путать.
                                          –2
                                          Я ничего не путаю. Даже автор статьи написал: «TS — всего-лишь надмножество JavaScript.». Каким бы крутым не был язык TS — он все равно компилируется в JS. И самым слабым звеном здесь является JS. Какой бы «syntax sugar» нам не давал TS, все равно все переводится в JS.
                                            +6

                                            Каким бы ни был крут Haskell, он все равно переводится в машинный код. Какой бы «syntax sugar» нам не давал Haskell, все равно все переводится в машинный код.

                                              0
                                              Коллега, причем тут это? Или это не удачная попытка «перевернуть» мои слова против меня?
                                                +1

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

                                                  0
                                                  «ибо есть транспилеры/компилеры во что угодно»
                                                  Согласен. Есть даже интересный проект на github.

                                                  «Вопрос, с чем мы работаем, и какие гарантии оно дает.»
                                                  Тогда, коллега, задам наводящий вопрос: «Почему TS компилирует в JS а не в машинный код?»
                                                    +1
                                                    Почему TS компилирует в JS а не в машинный код?

                                                    Где востребовано, туда и компилируется. Будет сильно нужно — будут через LLVM компилировать в бинарники. Why not?

                                                      –3
                                                      «Где востребовано, туда и компилируется.»
                                                      Вы сами ответили на свой вопрос! Поздравляю! Сила цепи определяется самым слабым звеном, что в нашем случае является JS.
                                                        +5

                                                        Г — Логика.

                                                          –3
                                                          Очень конструктивно. Жаль, что не могу минусовать.
                                                          +5
                                                          У вас странное представление.

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

                                                            Гарантирует ли «Static type checking» в TypeScript тот-же результат при «Dynamic type checking» в Javascript в Runtime?
                                                            То, что у вас все компилируется в TypeScript, совсем не означает, что в Javascript (Runtime) все будет работать. Слабым звеном является Javascript.
                                                              0
                                                              То, что у вас все компилируется в TypeScript, совсем не означает, что в Javascript (Runtime) все будет работать.

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

                                                              0
                                                              «Приобретем ли мы что-нибудь (кроме производительности), если сделаем машину, способную нативно исполнять хаскель-код?»

                                                              Я понял вашу мысль и вероятно не совсем понятно выразил свою.
                                                              +3
                                                              А машинный код — это тоже слабое звено?
                                                            0
                                                            «Вопрос, с чем мы работаем, и какие гарантии оно дает.»
                                                            Тогда, коллега, задам наводящий вопрос: «Почему TS компилирует в JS а не в машинный код?»

                                                            Вполне себе компилируется
                                                    +3

                                                    Вы и C++ можете в JS транспилировать.

                                              +2

                                              С каких пор такая зависимость должна решаться на уровне типов?


                                              Описываете обычный тип состоящий из всех четырёх параметров. При десериализации параметр other_param, в случает отсутствия, останется равной null. А вот уже в коде вы же перед использованием параметра проверите, param_flag равен true или нет?

                                                +1

                                                Почитайте про зависимые типы

                                                  0

                                                  Спасибо, прочёл. Но как это в коде реализуется?
                                                  Видимо тут пример неудачный, потому что, как уже говорил, я сходу вижу конкретный тип состоящий из 4 параметров. Просто в одном случае мы получим other_param равной null, а в другом — какому-то значению. И уже в коде мы в любом случае будет проверять other_flag равен true или нет перед использованием.


                                                  Или в динамических языках это делается как-то по другому? Мне (я серьёзно) интересно даже узнать.

                                                    0
                                                    Спасибо, прочёл. Но как это в коде реализуется?

                                                    Код знает какие типы могут быть. И может делать выводы о гарантиях. В typescript прочитайте про discriminated unions и type guards


                                                    https://www.typescriptlang.org/docs/handbook/advanced-types.html

                                                      0
                                                      Хм, ну type guards — это просто проверка типа получается?

                                                      Например, как в C#
                                                      Аналог instanceof:
                                                      if (some is Foo foo)
                                                      {
                                                         // Do something with foo.
                                                      }
                                                      

                                                      Аналог typeof:
                                                      if (some.GetType() == typeof(int))
                                                      {
                                                         // В данном случае это заменимо на is.
                                                      }
                                                      


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

                                                      А discriminated unions прям как в C#. Разве что выглядит чуть по другому:

                                                      Почти копия из TypeScript
                                                      interface IShape { }
                                                      
                                                      interface ISquare : IShape
                                                      {
                                                          double Size { get; set; }
                                                      }
                                                      
                                                      interface IRectangle : IShape
                                                      {
                                                          double Width { get; set; }
                                                          double Height { get; set; }
                                                      }
                                                      
                                                      interface ICircle : IShape
                                                      {
                                                          double Radius { get; set; }
                                                      }
                                                      
                                                      static double area(IShape shape)
                                                      {
                                                          switch (shape)
                                                          {
                                                              case ISquare square: return square.Size * square.Size;
                                                              case IRectangle rectangle: return rectangle.Width * rectangle.Height;
                                                              case ICircle circle: return Math.PI * circle.Radius * circle.Radius;
                                                              default: return double.NaN;
                                                          }
                                                      }
                                                      


                                                      Такая похожесть, скорее всего из-за того, что C# (судя по вики) повлиял на TypeScript.
                                                        0

                                                        Одна компания разработчик, один автор языков… Хмм… Да c# похоже все-таки повлиял на тайпскрипт!

                                                          +1

                                                          Нет, type guards — это вынос проверки типа во внешнюю функцию, применяются совместно со смарткастами. Аналогов в C# у этой техники нет, за отсутствием смарткастов.

                                                            0
                                                            Такая похожесть, скорее всего из-за того, что C# (судя по вики) повлиял на TypeScript.

                                                            Тут все как раз наоборот — это в C# гварды как в typescript (нормальный паттерн-матчинг не осилили, вот и прилепили гвардо-костыли) :). А в typescript — они как в Racket.

                                                          0

                                                          А я два типа из двух и трёх параметров и проверкой типа перед использованием. Код типа


                                                          function parseResponse(data) {
                                                            return some_flag ? new SomeClass(data) : new AnotherClass(data);
                                                          }
                                                          
                                                          const response = parseResponse(data);
                                                          if (response instance of SomeClass) {
                                                            console.log(response.id, response.otherFlag)
                                                          } else if (response instanceof AnotherClass) {
                                                            console.log(response.id, response.otherFlag, response.anotherParam)
                                                          }

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

                                                            0
                                                            Как я уже писал — использовать под-типы (композицию типов) для описания всех возможных состояний возможно только в очень маленьких проектах и не является самой сутью решения проблемы.
                                                            0
                                                            И уже в коде мы в любом случае будет проверять other_flag равен true или нет перед использованием.

                                                            Но в данном случае вам никто не мешает не проверить этот флаг перед использованием other_param, а с завтипами — мешает.

                                                              0

                                                              C таким типом, как выше приводили:


                                                              {
                                                                id: number;
                                                                some_flag: boolean;
                                                                other_flag: false;
                                                              } | {
                                                                id: number;
                                                                some_flag: boolean;
                                                                other_flag: true;
                                                                other_param: string;
                                                              }

                                                              не проверить флаг будет нельзя. И ЗТ никаких не требуется.

                                                                –1

                                                                Как это не требуется, когда вон оно торчит, где other_flag: false или true?

                                                                  0
                                                                  И что? Этого недостаточно чтобы называться завтипом…
                                                                    0

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


                                                                    Я чуть ниже напишу сейчас Druu подробнее.

                                                                    0

                                                                    Ну это не ЗТ.


                                                                    type Yoba = {
                                                                        id: number;
                                                                        some_flag: boolean;
                                                                        other_flag: false;
                                                                    } | {
                                                                        id: number;
                                                                        some_flag: boolean;
                                                                        other_flag: true;
                                                                        other_param: string;
                                                                    };
                                                                    
                                                                    function yoba(x: Yoba) {
                                                                        console.log(x.id) // ok, ид есть в обеих ветках
                                                                    
                                                                        console.log(x.other_param) // не компилируется
                                                                    
                                                                        if (x.other_flag) {
                                                                            console.log(x.other_param) // компилируется
                                                                        } else {
                                                                            console.log(x.other_param) // не компилируется
                                                                        }
                                                                    }

                                                                    здесь нет зт, это по сути аналог паттерн-матчинга. просто если при стандартных АТД когда у нас data YobaType = C1 | C2, то C1 и C2 не являются сами типами, это просто конструкторы. А вот в случае типов-объединений — это именно типы самостоятельные, по-этому конструктор C1 возвращает один тип, конструктор C2 — другой тип, оба эти типы — подтипы типа YobaType. С-но в хаскеле когда мы находимся в какой-то из веток паттерн-матчинга мы имеем типа YobaType (т.к. типов C1/C2 нету, сузить тип в ветке матчинга протсо не на что), а вот в ts компилятор может сузить тип до более конкретного, т.к. соответствующие типы есть.
                                                                    Ну и да, тут автолифтинг, то есть значения вроде true, false, undefined, 125, "yoba" автоматически лифтятся в соответствующий тип-синглтон, когда мы используем эти литералы в типовом контексте (вроде же кстати в хаскеле чот такое тоже прикручено в каком-то расширении).
                                                                    То есть в объявлении типа выше true/false — это, с формальной точки зрения, именно лифтанутые типы-синглтоны, а не значения.

                                                                      0
                                                                      Ну и да, тут автолифтинг, то есть значения вроде true, false, undefined, 125, "yoba" автоматически лифтятся в соответствующий тип-синглтон, когда мы используем эти литералы в типовом контексте (вроде же кстати в хаскеле чот такое тоже прикручено в каком-то расширении).

                                                                      Ну так этот паттерн с синглтонами — это как раз классический способ выражения ЗТ в языке без них нативных, но с GADT (например) и DataKinds.


                                                                      Тем более, что оно потом так же автоматически опускается обратно, когда вы в if проверяете, и с проверкой из if тайпчекер может вывести, какие операции над значением допустимы. Прям dependent pattern matching.


                                                                      То есть, как бы, понятно, что это вопрос определений, но как бы общепринятый консенсус [в хаскель-коммьюнити], что когда вы пишете что-то вроде


                                                                      {-# LANGUAGE GADTs, DataKinds #-}
                                                                      
                                                                      data Yoba otherFlag where
                                                                        FalseYoba :: { id :: Number, someFlag :: Bool } -> Yoba 'False
                                                                        TrueYoba :: { id :: Number, someFlag :: Bool, otherParam :: String } -> Yoba 'True

                                                                      то всё, приехали в славную завтипизированную страну.


                                                                      Кстати, конкретно в хаскеле это вы написать так не можете, ибо конфликты типов лейблов (хотя, теоретически, наверное, компилятор мог бы и унифицировать до, скажем, someFlag :: forall otherFlag. Yoba otherFlag -> Bool), так что тут х-ль соснул у ts.


                                                                      А вот в случае типов-объединений — это именно типы самостоятельные, по-этому конструктор C1 возвращает один тип, конструктор C2 — другой тип, оба эти типы — подтипы типа YobaType.

                                                                      А YobaType как записать можно?

                                                                        +1
                                                                        Ну так этот паттерн с синглтонами — это как раз классический способ выражения ЗТ в языке без них нативных, но с GADT (например) и DataKinds.

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


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


                                                                        А YobaType как записать можно?

                                                                        Ну так объединением и записывается, неразмеченным. Допустим, у нас есть два типа A и B, мы сперва каждому типу добавляем тег в виде типа-синглтона, т.о. получаем типы-кортежи <tag1, A> и <tag2, B> и потом эти типы объединяем через обычное (неразмеченное) объединение, получаем YobaType = <tag1, A> | <tag2, B> (опускаем всякие несущественные нюансы со структурным подтипированием, тем что у нас поля на самом деле будут именованные а не безымянный как в кортеже и т.п.).
                                                                        В случае когда мы при помощи АТД строим тип-сумму, то мы выполняем эти два действия в один шаг, по-этому типы <tag1, A> и <tag2, B> у нас просто не проявляются, то есть операция суммы — примитивная. В данном же случае она не примитивная и выражается через другие операции — умножения и объединения, в итоге у нас типы-компоненты объединения существуют.
                                                                        В примере выше у нас tag'ом выступает поле other_flag. Ну и тот факт, что внутри true-ветки внутри if мы знаем какой конкретно подтип у значения — он тождественен тому факту, что в ветке паттерн-матчинга мы знаем, что значение может быть рпазобрано паттерном этой ветки. Просто в случае хаскеля мы этот факт (что значение х может быть разобрано паттерном Y) не можем выразить в виде утверждения вида "значение х имеет тип Y", т.к. нету подходящего типа Y. А в тс такой тип находится.


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


                                                                        так что тут х-ль соснул у ts.

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


                                                                        filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];

                                                                        и тут с-но вопрос, что значит это "value is S"? а это значит, что данная ф-я является гвардом (т.е. предикатом над T и истинность этого предиката влечет за собой сужение типа Т до типа S). Иными словами, мы можем подать на вход фильтру массив (A | B)[], гвард isA в качестве фильтра и получим массив A[] на выходе.


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


                                                                        function isZero(x: number): x is 0 {
                                                                            return true;
                                                                        }
                                                                        
                                                                        const yoba = [1, 2, 3, 4, 5].filter(isZero);

                                                                        yoba: 0[] :)

                                                                          0

                                                                          Не, забавный этот ваш TS, я был о нём худшего мнения.


                                                                          Я знаю, что тут есть другая точка зрения, но сам считаю, что зависимые типы — они только тогда, когда есть фичи, которые без лямбда-P не реализуются.

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

                                                                            +1
                                                                            Например?

                                                                            Ну классическое, мы сможем как-нибудь описать Vec[N], создать вектор длины N с соответствующим типом, где N считано из файла, и потом сделать type-safe доступ по индексу?

                                                                              0
                                                                              И как там с этим в TS — сможем? В хаскеле вот сможем.

                                                                              А просто взять и поднять функцию на термах в типы не сможем, кстати, хотя для многих функций сможем переписать её на аналогичную с семействами типов и прочей наркоманией, и сможем сделать это внутри языка. Но это уже такое, практическое.
                                                                                –1
                                                                                В TS — разумеется, не сможем, потому что к тому моменту, когда нам известен размер массива, никакого TS уже нет.
                                                                                  0
                                                                                  И как там с этим в TS — сможем?

                                                                                  Нет.


                                                                                  В хаскеле вот сможем.

                                                                                  А можно результат посмотреть, с-но?

                                                                                    0
                                                                                    А можно результат посмотреть, с-но?

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


                                                                                    Получается что-то такое:


                                                                                    {-# LANGUAGE GADTs #-}
                                                                                    {-# LANGUAGE KindSignatures #-}
                                                                                    {-# LANGUAGE DataKinds #-}
                                                                                    {-# LANGUAGE TypeOperators #-}
                                                                                    {-# LANGUAGE ScopedTypeVariables #-}
                                                                                    {-# LANGUAGE TypeFamilies #-}
                                                                                    {-# LANGUAGE ConstraintKinds #-}
                                                                                    {-# LANGUAGE TemplateHaskell #-}
                                                                                    
                                                                                    module Main where
                                                                                    
                                                                                    import Data.Kind
                                                                                    import Data.Singletons
                                                                                    import Data.Singletons.TH
                                                                                    
                                                                                    data Nat = Z | S Nat
                                                                                    
                                                                                    $(genSingletons [''Nat])
                                                                                    
                                                                                    type family (m :: Nat) <@? (n :: Nat) :: Bool where
                                                                                      'Z   <@? 'S _ = 'True
                                                                                      'S m <@? 'S n = m <@? n
                                                                                      _    <@? _    = 'False
                                                                                    
                                                                                    type x <@ y = (x <@? y) :~: 'True
                                                                                    
                                                                                    data Vector (t :: Type) (a :: Nat) where
                                                                                      VNil :: Vector t 'Z
                                                                                      (:::) :: t -> Vector t n -> Vector t ('S n)
                                                                                    
                                                                                    infixr 9 :::
                                                                                    
                                                                                    safeIndex :: k <@ n -> SNat k -> Vector t n -> t
                                                                                    safeIndex Refl SZ     (v ::: _)  = v
                                                                                    safeIndex Refl (SS n) (_ ::: vs) = safeIndex Refl n vs
                                                                                    
                                                                                    data SomeVector t where
                                                                                      SomeVector :: SNat n -> Vector t n -> SomeVector t
                                                                                    
                                                                                    makeSomeVector :: SomeVector Char
                                                                                    makeSomeVector = SomeVector sing ('t' ::: 'o' ::: 'p' ::: 'k' ::: 'e' ::: 'k' ::: VNil)
                                                                                    
                                                                                    consumeSomeVector :: SomeVector Char -> (Int, String)
                                                                                    consumeSomeVector (SomeVector natSing vec) = undefined

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


                                                                                    Сравнение синглтонов надо писать руками, но это делать мне уже стало лень, принцип там тот же. С таким сравнением уже можно написать consumeSomeVector.

                                                                                      0

                                                                                      Хм, я сейчас посмотрел и понял, что походу через рефлы можно и на тс как-то такое оформить. Подумаю на досуге.


                                                                                      ЗЫ: кстати, для работы еще нужна ф-я length специальная, т.к. без нее вы просто не сможете сконструировать рефл для массива, длина которого в компайлтайме не известна. Какой у нее тип будет?

                                                                                        0
                                                                                        А что это вы тут такое пытаетесь сделать?
                                                                                          0

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

                                                                                            0
                                                                                            fixed size array? Так в TS есть кортежи, которые он чекает. Или хочется именно циферкой задавать размер?
                                                                                              0
                                                                                              Думаю, именно цифрой. Чтобы также возможно было написать типизированную версию addElement, как вот тут
                                                                                                0
                                                                                                А, ну на TS такое не реализуемо из-за отсутствия арифметики времени компиляции. Хотя уж куда-куда, а к TS исполнение времени компиляции было бы прикрутить проще всего. Но они там себе на уме, опыт других языков не используют — проходят всю эволюцию самостоятельно.
                                                                                                0
                                                                                                fixed size array? Так в TS есть кортежи, которые он чекает.

                                                                                                Размер кортежей известен во время компиляции.


                                                                                                А, ну на TS такое не реализуемо из-за отсутствия арифметики времени компиляции.

                                                                                                По факту она (конкретно арифметика) и в примере на хаскеле не совсем чтобы важна.

                                                                                                  0
                                                                                                  По факту она (конкретно арифметика) и в примере на хаскеле не совсем чтобы важна.

                                                                                                  Ну как, семейство <@? там именно эту арифметику (вернее, её маленький кусочек) и реализует.

                                                                                                  0

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

                                                                                              0
                                                                                              ЗЫ: кстати, для работы еще нужна ф-я length специальная, т.к. без нее вы просто не сможете сконструировать рефл для массива, длина которого в компайлтайме не известна. Какой у нее тип будет?

                                                                                              Ну там как раз совсем всё просто, демоутите синглтон и получаете value:


                                                                                              vlength :: forall t n. SingI n => Vector t n -> Nat
                                                                                              vlength _ = fromSing (sing :: Sing n)

                                                                                              forall там нужен для правильного скоупинга n, чтобы в теле функции оно обращалось к n из сигнатуры (такое использование этого ключевого слова меня почему-то всегда пробивает на поржать, haskell is the new C++).


                                                                                              С очевидной natToInt всё работает как ожидается:


                                                                                              *Main> natToInt $ vlength ('t' ::: 'o' ::: 'p' ::: 'k' ::: 'e' ::: 'k' ::: VNil)
                                                                                              6

                                                                                              Правда, рефлы делать вам это всё равно не поможет, потому что для рефлов нужно доказательство на уровне типов (ну как вон те <@ и <@? у меня), а термы вам с этим не помогут, вам их придётся обратно заворачивать в синглтоны, и вы ничего не выиграли. Рассуждайте сразу в терминах синглтонов.


                                                                                              Либо, если вы хотите vlength на уровне синглтонов, то у вас будет аналогичный SomeVector экзистенциальный тип вроде SomeLength, на котором нужно паттерн-матчить, чтобы получить длину.




                                                                                              Кстати, совершенно неотносящееся, но, возможно, вам будет забавно. Я тут книжку по вычислимым функциям читаю, и там есть забавная задачка, над которой я периодически раздумываю последние пару дней и что-то никак не могу решить. Так вот, если взять функцию U(n, x), универсальную в классе вычислимых функций и главную, то довольно легко показать, что существует алгоритм, по двум U-номерам функций дающий некоторый U-номер их композиции. Однако оказывается, что верно и обратное: если U — универсальная, и существует алгоритм, по двум номерам дающий номер их композиции, то U — главная. Как это доказать?

                                                                                                –2
                                                                                                Эта ветка — лучшая иллюстрация того что статическая типизация вредна для большинства проектов.
                                                                                                  +2
                                                                                                  В этой ветке пытаются поонанировать вприсядку после тройного тулупа в перевороте совершенно непредназначенными для этого инструментами просто из любви к искусству. Делать какие-то далеко идущие выводы на базе этого несколько странно.

                                                                                                  С предназначенными для этого инструментами всё очень просто.
                                                                                                    0
                                                                                                    Что именно очень просто?
                                                                                                      0

                                                                                                      Решить обсуждавшуюся задачу.

                                                                                                        0
                                                                                                        Так, и в чём она состояла?))
                                                                    +1
                                                                    «С каких пор такая зависимость должна решаться на уровне типов?»
                                                                    А с каких пор она НЕ ДОЛЖНА решаться на уровне типов?

                                                                    «Описываете обычный тип состоящий из всех четырёх параметров.»
                                                                    Вы работали с Haskell? Если бы работали, то не констатировали эту чепуху. То, что это реализуемо в JS (flow, typescript), не означает, что такое прокатит в других языках, где есть типы.

                                                                    Мой изначальный вопрос ничего общего не имел с js.
                                                                      0

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

                                                                        0
                                                                        Благодарю за Idris, не знал о таком. Жаль, что эти псевдо-эксперты типизированных языков, которые накидали мне минусов, типы использовали только в JS и думают, что знают о типизации все. По вам видно, что вы — человек с опытом и знающий.
                                                                    0
                                                                    Я не фронтендер ни разу, но когда возникает необходимость, делаю примерно так, если исходить из вашего примера:

                                                                    function MyType() {
                                                                        this.id = -1;
                                                                        this.some_flag = false;
                                                                        this.other_flag = false;
                                                                        this.other_param = '';
                                                                        
                                                                        this.map = function (entry) {
                                                                            this.id = entry.id;
                                                                            this.some_flag = entry.year;
                                                                            this.other_flag = entry.grade;
                                                                            if(this.other_flag)
                                                                                this.other_param = entry.other_param ;
                                                                        };
                                                                    }


                                                                    Так как обмен данными формализован, набор полей которые может вернуть сервер известен, описываю тип в котором есть все поля которые могут придти, и после приема данных вызываю map, в нем можно доделать нужные трансформации и присвоить к полям типа который будет использоваться уже в JS. Ну и докинуть все нужные проверки.
                                                                      +1
                                                                      И какую проблему решает данный этюд?
                                                                        –1
                                                                        Позволяет привести к строгому типу принятые данные. И добавить при необходимости валидацию.
                                                                          0
                                                                          Строгий тип не проблема, хоят в некоторых случаях может быть решением. Про валидацию не понял, сервер отвечает
                                                                          {
                                                                              ...
                                                                              this.other_flag = false;
                                                                              this.other_param = 'Deal with it';
                                                                          }
                                                                          

                                                                          И кусок ответа молча выкидывается.
                                                                  +1
                                                                  в проектах совсем мало ошибок, которые уберёт статическая типизация
                                                                  И это очень важный фактор, с которым, судя по «дебатам», не согласны сторонники статической типизации.
                                                                  –1

                                                                  К сожалению без "as any" и "// @ts-ignore" в TypeScript пока не обойтись.
                                                                  Потому что существуют библиотеки вроде React, в которых типизация поставляется отдельно (в пакетах "@types/..."). И часто типизация написана неаккуратно или отстает от последней версии библиотеки.

                                                                    +8
                                                                    Так напишите свой интерфейс в своем проекте и прикастите к нему. Получите и строгую типизацию без any и минимум заморочек. И чужой код править не надо.
                                                                      +6

                                                                      Специально для таких случаев существует Declaration Merging https://www.typescriptlang.org/docs/handbook/declaration-merging.html. Не нравится как описан какой то тип из тайпинов? Допиши сам!
                                                                      А ставить any или того хуже @ts-ignore это подкладывать грабли себе же самому

                                                                        +1

                                                                        А хотите пример? React useRef(). На flow он выглядит так (исходник):


                                                                        function useRef<T>(initialValue: T): { current: T };

                                                                        В @types/react так:


                                                                        function useRef<T>(initialValue: T): { current: T; };
                                                                        function useRef<T>(initialValue: T | null): { readonly current: T | null; };

                                                                        А теперь попытаемся открыть в TypeScript пример из документации


                                                                        const intervalRef = useRef();
                                                                        intervalRef.current = setInterval(() => {});

                                                                        Для Flow все нормально, он даже типы выведет. А TypeScript скажет: [ts] Expected 1 arguments, but got 0. И пока саму библиотеку и ее типизацию поддерживают разные люди, такое будет встречаться регулярно.


                                                                        Или вот прекрасная библиотека mobx-state-tree. Все в ней хорошо: и реактивность, и снапшоты, и статическая типизация, и проверки типов в runtime. Но похоже, что ее TypeScript интерфейсы не являются публичным API. Несмотря на то, что они эскпортированы из модуля. Да и сама библиотека написана на TypeScript.


                                                                        Внезапно, между двумя минорными версиями 3.5.0 и 3.6.0 мы видим:


                                                                        - export interface IMSTMap<C, S, T> {
                                                                        + export interface IMSTMap<IT extends IAnyType> {

                                                                        Ну и как таким пользоваться?


                                                                        Это все не проблема TypeScript как языка. Это проблема экосистемы в целом. Я до такого докопаюсь. Возможно, напишу свои декларации. Буду их скрупулезно поддерживать. Но придет мой коллега из лагеря динамической типизации, и скажет: "Почему я должен тратить время на все это?"

                                                                          0
                                                                          Ну и как таким пользоваться?

                                                                          Могу предложить вот что:
                                                                          1. Сделайте временный workaround, закомментируйте его.
                                                                          2. Откройте issue в трекере DefinitelyTyped.
                                                                          3. Откройте issue в трекере Facebook c предложением уже забить на Flow. Правда, учитывая, что их год просили добавить поддержку TypeScript в create-react-app, вряд ли они отрегируют в ближайшее время.
                                                                          4. После того, как будет исправлено, уберите workaround.

                                                                          Это проблема экосистемы в целом.

                                                                          Если мы будем искать экосистему без проблем, то не напишем ни одной строчки.
                                                                            0

                                                                            Да есть уже у них issue. Просто утомляет это все очень =(

                                                                        +1

                                                                        Так не используйте такие библиотеки, какие проблемы?

                                                                          +3
                                                                          все там нормально у реакта с тайпингами :)
                                                                          +31
                                                                          Типы рождаются не из ключевого слова class/struct/type а из вашей головы. Когда вы думаете о задаче, вы сводите ее к набору категорий, взаимодействующих по определенным правилам. Это относится не только к программированию, это способ мышления вообще. Если мы думаем об обществе, мы не думаем о каждом человеке по отдельности. Поэтому никого не удивляет что у человека есть метод «спать», а у общества нет. Поэтому, даже используя JS я создаю категории объектов которые типами не называются лишь номинально.
                                                                          Вы начали с Шарпа и строгая статическая система типов потребовала от вас умения проводить свои мысли в порядок. Те же кто начал со скрипта и не имел врожденной склонности к порядку получили в голове кашу, причесать которую очень сложно. поэтому их код похож на доску конспиролога.
                                                                            +14
                                                                            Добавлю ещё что ООП и типизация перпендикулярные понятия. ООП получается если прикрутить код к данным (объекты носят свой код с собой). Это можно сделать на любом современном языке. Где-то проще, где-то сложнее.
                                                                              +6
                                                                              Можно написать достаточно удобную и типизированную программу даже на классическом Бейсике с двухбуквенными имеными переменных и отсуствием структур. А можно — кашу на любом «строгом» языке типа Ады.

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

                                                                              Попытка построить таким путём мост длиной в 10 километров приведёт к тому, что его шторм унесёт в море задолго до того, как вы закончите его строить… а попытка строить мост через ручей «по феншую» приведёт к тому, что он обойдётся в 100 раз дороже… хотя и стоять будет дольше — но это не всегда оправданно.
                                                                                0
                                                                                aikixd
                                                                                Добавлю ещё что ООП и типизация перпендикулярные понятия. ООП получается если прикрутить код к данным (объекты носят свой код с собой).
                                                                                Истинно так. ©
                                                                                В JS незачем тащить никакое ООП как стандарт. Есть внешняя JS библиотека (а может их и много существует), используя которую, вы можете «породить» такое ООП (со своими правилами «наследования», «использования кода объектов») которое только придумаете сами!

                                                                                Незачем «приколачивать» конкретную реализацию ООП в стандарт JS. Это уже(!) смысла не имеет никакого. — Хватит, что бессмысленный «const» ввели в стандарт JS.

                                                                                JS чем хорош — что это свободный(!) язык. Как там говорится — используя JS вы можете делать что захотите, в том числе и плохие вещи — не каждый язык программирование это позволяет, да ещё в таких(!) масштабах. ;-)
                                                                                +3
                                                                                Те же кто начал со скрипта и не имел врожденной склонности к порядку получили в голове кашу, причесать которую очень сложно. поэтому их код похож на доску конспиролога.

                                                                                Понимаете ли какое дело… Будь у этих ребят в голове каша, мы бы не видели весьма сложных и нетривиальных библиотек ни на js, ни на питоне, ни на чём-то подобном. Но мы их ВИДИМ. И КАК они разработаны, для меня, как приверженца типов и статики, полная загадка… Возможно я чего-то в этой жизни не понимаю. И в этом мне дико хотелось бы разобраться, хотя сам скорее всего так со статикой и останусь.
                                                                                  0
                                                                                  Те кто писал эти библиотеки, действительно талантливые программисты. И библиотеки эти создают некий порядок в проектах, который хоть как-то спасает. И заодно прочности у них хороший, так как насилуют их нещадно.
                                                                                    +2
                                                                                    Он же не пишет, что у всех программистов на динамически типизированных языках в голове каша. Каша у тех, кто не может в самодисциплину. Статическая типизация дисциплину навязывает, а динамическая — нет, приходится самому.
                                                                                    –1

                                                                                    Откуда вы знаете, может это private метод, к которому просто никто не обращался)

                                                                                      0
                                                                                      Те же кто начал со скрипта и не имел врожденной склонности к порядку получили в голове кашу, причесать которую очень сложно. поэтому их код похож на доску конспиролога.

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

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

                                                                                      На самом деле, думаю, что вы совсем не одиноки. Я уже как-то скидывал однажды картинку из доклада Романа Елизарова про развитие ЯП, скину еще раз, так как она весьма показательна.
                                                                                      Топ 20 языков программирования и их типизация
                                                                                      image

                                                                                      Чертой отделены языки, вышедшие в 20 и 21 веках. Здесь отлично видно, что индустрия в последнее время однозначно делает выбор в пользу статической типизации. Добавим сюда факты, что в своем развитии php пришел к строгой типизации, в python вроде добавляли статическую типизацию, в мире фронтенда набирают популярность ts, dart, flow, в реакте сделан тайпчекинг. Ну, и наконец, учтем тяготение императивных современных языков к функциональщине, где есть стремление выражать возможные состояния через типы и сама система типов еще более строгая.

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

                                                                                        Системы типов в мейнстрим языках становятся всё более развитыми и позволяют всё большую гибкость (= меньше напрягают, больше помогают), поэтому остаётся всё меньше поводов выбирать динамическую типизацию.
                                                                                          +10
                                                                                          Вопрос интерпретации. Я вот из картинки вижу только, что языки со статической типизацией сильно не удовлетворяют нуждам, поэтому народ постоянно выдумывает новые языки.
                                                                                          В то время как тот же Javascript быстро развивается (это уже не тот язык что был даже 5 лет назад), а его экосистема растёт экспоненциально. TypeScript же — просто калька, занимающая нишу подражания (старо-)новомодной типизации.
                                                                                            +10
                                                                                            Интересная точка зрения.
                                                                                            В свою очередь, моя интерпретация вашей интерпретации, связанной с экспоненциальным ростом экосистемы Javascript, заключается в том, что без вагона костылей на Javascript написать ничего вменяемого ни у кого не получается. Отсюда и перманентные попытки придумать серебряную пулю.
                                                                                              +1
                                                                                              Что является следствием того что JS был много лет единственным языком для фронта. В свою очередь его архитектура является следствием того, что нужно было обеспечить быструю интерпретацию на слабых машинах конца девяностых.
                                                                                                +4
                                                                                                Интересно девки пляшут… И как же по-Вашему динамическая и никак не выводимая из контекста типизация помогает быстро интерпретировать код? Наоборот, в рантайме перед каждой операцией придётся ещё проверить, что именно в переменой лежит, если надо сделать какие-то умолчательные преобразования и т.д. А вот то что для подобного языка гораздо проще и быстрее написать интерпретатор, это да. Сильно подозреваю, что с js именно так оно и было.
                                                                                                  +1
                                                                                                  Сильно подозреваю, что с js именно так оно и было.
                                                                                                  А не надо подозревать. Брендан сам рассказывает, что у него было 10 дней на всё-про-всё.

                                                                                                  Получился, конечно, уродец тот ещё… но могло быть и хуже…
                                                                                                –3
                                                                                                Костыли js это результат того, что языку приходится решать задачи выше его компетенции, и только во фронте, как тока браузеры на 100% договорятся и будут следовать последней версии спецификации никаких костылей не будет.
                                                                                                TS на бэке используют только консерваторы типа автора статьи.
                                                                                                  0

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

                                                                                                +9
                                                                                                > В то время как тот же Javascript быстро развивается

                                                                                                В чем это выражается? JS за последние 5 лет что-то принес остальному миру, новые идеи, практики? Или он растёт только относительно себя же?

                                                                                                > экосистема растёт экспоненциально

                                                                                                Рост экосистемы ценен тогда, когда каждый элемент работает из коробки, а вместе дают синергетический эффект. А когда «часто типизация написана неаккуратно или отстает от последней версии библиотеки» — это рост в сторону бесконечного решения специфической «проблемы ромба» из за несовместимости кусочков инфраструктуры и вечных войн тупоконечников с остроконечниками, в каждой итерации заканчивающихся созданием новых, несовместимых со всем остальным, кусочков. И переписывания старых на TypeScript.
                                                                                                А есть ли за всем этим синергетический эффект? Или только экспоненциальный рост сложности?
                                                                                                  +1
                                                                                                  В чем это выражается? JS за последние 5 лет что-то принес остальному миру, новые идеи, практики? Или он растёт только относительно себя же?

                                                                                                  Вот тут пожалуй с Вами не соглашусь. Да, развивается. И пожалуй да, быстрее чем всё прочее вместе взятое. Поглядите на библиотеки! То что js ИСКЛЮЧИТЕЛЬНО гибок, этого у него не отнять. Гибче пожалуй только Forth(хотя на мой взгляд это гибкость самоубийцы). И именно потому что бурно развивается, мне очень хочется понять ПРИЧИНЫ такой дикой популярности… Чисто любопытства ради.
                                                                                                    +2
                                                                                                    Поглядите на библиотеки!
                                                                                                    На какую стоит посмотреть, чтобы увидеть что-то новое и интересное?

                                                                                                    Пока что всё, что я видел — это, в основном, новые способы сделать то, что раньше можно было уже сделать и так — только с новым синтаксисом и порождением большего количества кода в результате.
                                                                                                      0
                                                                                                      Вы имеет ввиду библиотеки вроде left-pad?
                                                                                                        0

                                                                                                        В процитированном вами тексте находятся одни вопросы. Как можно "не соглашаться с вопросами"?

                                                                                                          +1

                                                                                                          С риторическими — прекрасно можно!

                                                                                                            0
                                                                                                            1. Можно пример?
                                                                                                            2. Как вы определяете считается вопрос риторическим или нет? Решаете ли вы тем самым за вашего собеседника ожидает ли он ответа на вопрос? Если да, то по какому собственно праву?
                                                                                                              +1
                                                                                                              Можно пример?

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


                                                                                                              Ритори́ческий вопро́с — риторическая фигура, представляющая собой вопрос-утверждение, который не требует ответа.

                                                                                                              такие дела


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

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


                                                                                                              Если да, то по какому собственно праву?

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

                                                                                                          +1
                                                                                                          js ИСКЛЮЧИТЕЛЬНО гибок

                                                                                                          В чем же состоит ИСКЛЮЧИТЕЛЬНАЯ гибкость самого JS, которая оказывается вдруг не свойственна остальным языкам общего назначения?
                                                                                                            +9

                                                                                                            Полагаю автор комментария выше имел ввиду, что в JS в runtime вы можете вытворять практически всё, что угодно, кроме перегрузки операторов (её нет). Всякие там proxy, символы, defineProperty с хитрыми полями, getter-ы и setter-ы, toPrimitiveValue|toString|valueOf и пр. позволяют с виду простой очевидный донельзя код заставить делать что-то совершенно иное. Чем-то сродне define true false но в runtime. Весь этот хаос может как позволить написать супергибкие решения, так и опрокинуть вас в пучину хаоса с использованием минимума кода. Мне кажется, что JS это язык с беспредельным количеством анархии. Этакий godmod. Даже прототипная система наследования устроена так, что вы можете менять предков, поля\методы предков в мановению щелчка пальцев. Вы можете легко переопределять почти любые объекты и методы из "стандартной библиотеки". Ну в общем если в двух словах — уровень допустимого беспредела неограничен. И всё это в runtime. Хорошо это или плохо — судите сами.


                                                                                                            За другие языки "общего назначения" не скажу. Полагаю таких языков полно.

                                                                                                              +1
                                                                                                              Не знаю насчёт «общего назначения», но, когда я увидел, как можно вывернуть наизнанку код на Ruby, вмонтированный в RPG Maker (обычный Ruby, только немного урезанный), мысли были в общем и целом схожие.
                                                                                                                +1
                                                                                                                в JS в runtime вы можете вытворять практически всё, что угодно,… Всякие там proxy, символы, defineProperty с хитрыми полями, getter-ы и setter-ы, toPrimitiveValue|toString|valueOf и пр.

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

                                                                                                                Вы можете легко переопределять почти любые объекты и методы из «стандартной библиотеки».

                                                                                                                Манкипатчинг вроде считается плохой практикой и часто используется, емнип, для костылей, вроде полифилов. Но это локальная проблема веб-фронтенда.
                                                                                                                  +1
                                                                                                                  но на языках с полноценным рефлекшном, вроде C#/Java

                                                                                                                  Я не знаток C#/Java, но разве рефлексия в этих языках про это? Беглый гуглёж пишет: "В мире .NET рефлексией (reflection) называется процесс обнаружения типов во время выполнения". Да и судя по примерам там сплошные: IsGeneriсParameter, GetNestedTypes, InvokeMember, GetType и подобные штуки. Т.е. скорее анализ существующих методов. Я выше писал про вещи соооовсем другого порядка.

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

                                                                                                                    Т.е. скорее анализ существующих методов.

                                                                                                                    Развитые типобезопасные аналоги in, hasOwnProperty и isPrototypeOf и прочих.

                                                                                                                    Я выше писал про вещи соооовсем другого порядка.

                                                                                                                    Приведите пример задачи, которую по-вашему можно выполнить в JS, но нельзя, скажем, в C#.
                                                                                                                    Конечно, синтаксически языки отличаются, но обеспечить ту же семантику возможно. Например, в JS Object — это по сути Dictionary, написать ему обертку с прототипами, про модификацию свойств, итерирование, упоротый кастинг в соответствующие примитивы и будет так же весело.
                                                                                                                      0
                                                                                                                      Приведите пример задачи, которую по-вашему можно выполнить в JS, но нельзя, скажем, в C#.

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


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

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


                                                                                                                      Другой вопрос — а оно надо вообще? Вся эта анархия. Оставлю его за кадром. Речь шла о гибкости. И да, JS ну прямо очень гибкий. Можно построить какого угодно, даже неудовлетворенного желудочно, кадавра из практически любого объекта, что попадётся под ноги. Любителям извращений JS подходит на пятёрочку.

                                                                                                                        0
                                                                                                                        Ну и работать это будет только на вашем отдельно взятом велосипеде, верно?

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

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

                                                                                                                        Наследоваться, проксировать, покрывать фасадом. В зависимости от задачи. Не нужно искать прямых синтаксических аналогов JS здесь)
                                                                                                                          0

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

                                                                                                                            +2
                                                                                                                            В реальной жизни я не буду лезть в кишки чужой имплементации ибо: а) это риски на будущее, б) могут быть лицензионные ограничения.

                                                                                                                            Но даже если упоролся, у меня нет причины заниматься манкипатчингом в рантайме, я сделаю правки в исходниках библиотеки.
                                                                                                                              0
                                                                                                                              То есть нельзя. Аминь.
                                                                                                                                0
                                                                                                                                Вообще, строго говоря, можно.
                                                                                                                                Рефлексия позволяет сделать всё, включая прямую генерацию IL-кода.
                                                                                                                                Я не идеально разбираюсь в процессе, но из кажущихся решений — можно подменить вызов метода своим методом.
                                                                                                                                Но надо понимать, что это code injection со всеми связанными с этим особенностями, и относится больше к области хакинга.
                                                                                                                                Мне это понадобилось ровно один раз — в стандартной библиотеке работы с сетью при https запросе внутри происходит два http запроса, и между ответом первого и отправкой второго не прокидываются куки. А мне очень надо было. После быстрого гуглежа и попытки — я получил ошибку с крашем стека приложения и сделал всё совершенно иначе.
                                                                                                                                После чего открыл для себя возможность поправить и перекомпилять уже собранную сборку через дизассемблирование парой хитрых инструментов, и больше вообще не запаривался этим вопросом.
                                                                                                                              0
                                                                                                                              > поля приватные

                                                                                                                              Это рефлекшену разве мешает?

                                                                                                                              Ну а вообще, можно хоть Microsoft Fakes в боевой код притащить. Но могут побить.
                                                                                                                          +1

                                                                                                                          Вот например в эко-системе redux необходимо работать с immutable-данными. Каждая манипуляция над данными приложения должна менять весь store-приложения цельно (привет ФП). По сути это что-то вроде 1 переменной на всю "программу". А т.к. JS это не Haskell, то код, позволяющий этого добиться, выглядит… несимпатично, если сказать мягко. Та же картинка, я полагаю, со всеми императивными языками. Так вот, в JS отдельно взятые авторы написали велосипед, позволяющий писать визуально мутабельный код, оставив его под капотом иммутабельным. Работает это за счёт использования механизма Proxy. В итоге:


                                                                                                                          store.list[12].someValue = 12;
                                                                                                                          // =>
                                                                                                                          const newList = [...list];
                                                                                                                          newList[12] = { ...list[12], someValue: 12 };
                                                                                                                          const newStore = { ...store, list: newList };

                                                                                                                          Мы казалось бы явным образом задали 12 элементу массива в поле someValue число 12, а на деле произошло нечто совершенно другое. Просто каждая манипуляция с store трекалась, и вместо реальных объектов всё время подсовывались Proxy-обёртки, которые все операции записи превращали в down-up rewrapping.


                                                                                                                          Дичь? Ну наверное. По словам автора это даже довольно быстро работает. В C# получится написать такой костыль?

                                                                                                                            0

                                                                                                                            Насчет скорости и "довольно быстро работает" тут еще подумать надо, но синтаксически вот так сделать получится без особых проблем:


                                                                                                                            var newStore = store.Set(s => s.list[12].someValue, 12);
                                                                                                                              0

                                                                                                                              Ну вы сравнили. У вас какой-то Immutable.JS получился. Даром не нужен :)

                                                                                                                                0

                                                                                                                                А что не так-то? 9 лишних символов (.Set(s>,)) так принципиальны?

                                                                                                                                  0

                                                                                                                                  А всё не так. В реальном redux-reducers коде у вас только эти 9 символов, помноженных на каждую мутацию, и останутся. За ними мало что разглядеть можно будет. В гробу я видал такую гибкость и наглядность. redux-reducers в себе хранят по сути всю бизнес-логику приложения. Т.е. весь треш и угар как раз здесь. Чем более наглядно здесь описана логика, тем проще с этим работать. Так что да, более чем принципиальны. А если ещё вспомнить, что это поделие (immutableJS) тормозит, то я вообще не понимаю, зачем им кто-то пользуется.

                                                                                                                                    0

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

                                                                                                                                      0

                                                                                                                                      Ну тут я погорячился, да :)

                                                                                                                              +2
                                                                                                                              Звучит действительно дико)
                                                                                                                              Решение влоб — через обертку над стором, который бы внутри пересоздавался на каждое изменение. Основная проблема, над которой придется пободаться — это поддержка синтаксиса, ибо для поддержки кастомного сеттера проксировать необходимо весь граф объектов внутри. Но это должно быть решаемо и сильно зависит от исходного API доступа к свойствам.

                                                                                                                              ЗЫ Но, да, в .NET вряд ли появится redux, ибо глобальное состояние — это отличный маркер плохого дизайна и за это там гоняют тряпками. У JS-сообщества это еще впереди.
                                                                                                                                0
                                                                                                                                Так вот, в JS отдельно взятые авторы написали велосипед, позволяющий писать визуально мутабельный код, оставив его под капотом иммутабельным.

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

                                                                                                                                  –1

                                                                                                                                  Мутабельный код в JS выглядит проще, писать его легче и быстрее, читать и понимать тем более. За счёт простоты и наглядности в нём потенциально меньше багов. При этом в redux нам нужен именно иммутабельный код, для нормального функционирования системы. Этот инструмент призван помочь усидеть на двух стульях сразу. Дёшево и эффективно писать бизнес-логику не лишаясь преимуществ flux-подхода.


                                                                                                                                  Справляется или нет он с этой задачей — не знаю. Я не пробовал это в деле. Запомнил как пример самой яркой дичи в JS для продакшна, что мне только попадалась.

                                                                                                                                    +1
                                                                                                                                    Вообще-то тут речь идёт не о «мутабельном коде», а о «мутабельных переменных».

                                                                                                                                    Работать с которыми сложнее, отлаживать сложнее и понимать сложнее.

                                                                                                                                    Но при большом желании всё это делается и на C++ (через шаблоны) и на C#/Java (через reflection)
                                                                                                                                      0

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

                                                                                                                                      –1
                                                                                                                                      Мутабельный код в JS выглядит проще, писать его легче и быстрее, читать и понимать тем более.

                                                                                                                                      Почему бы тогда просто не писать просто мутабельный код, который будет просто мутабельно работать?


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

                                                                                                                                        +1
                                                                                                                                        Смысл иммутабельности именно в том, что код пишется как иммутабельный.

                                                                                                                                        Что? Я вас не понимаю. Иммутабельность нужна для… иммутабельности? Что?


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

                                                                                                                                          0
                                                                                                                                          Что? Я вас не понимаю. Иммутабельность нужна для… иммутабельности? Что?

                                                                                                                                          Я говорю, не важно как у вас что работает ВНУТРИ. Пусть оно хоть миллион раз будет мутабельно — это не важно. Иммутабельность ценна именно интерфейсом — тем, как мы работаем с данными.
                                                                                                                                          И если мы работаем с данными мутабельно, то никакого толка от того, что где-то внутри там что-то иммутабельно — нет, все профиты сразу исчезают. С другой тсороны, если у нас интерфейс выглядит как иммутабельный, хотя внутренняя реализация мутабельна — мы все профиты иммутабельности имеем. С-но это и есть идеальный вариант "на двух стульях" — пишем иммутабельный код, который по факту мутабельно работает. Попытка же сделать наоборот — это попытка отобрать худшее.

                                                                                                                                            +1

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


                                                                                                                                            У редьюсеров иммутабельный интерфейс — он требуется редуксом. А вот внутри редьюсера можно уже писать как угодно — при условии сохранения интерфейса.

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

                                                                                                                                              И к чему вы это?


                                                                                                                                              У редьюсеров иммутабельный интерфейс — он требуется редуксом.

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

                                                                                                                                              0

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


                                                                                                                                              Затем, как вы можете потерять "профиты" от иммутабельности, если под капотом работает иммутабельность. Куда они денутся? Если вместо мутации объектов вы используете новые, то ссылочная целостность никуда не девается, pure-функций не становятся impure-функциями.


                                                                                                                                              Ну и касательно — писать иммутабельный код, который на самом деле под капотом мутабельный… Ух. Я себе если честно даже представить себе не могу, что это. И уж тем более не могу представить зачем.


                                                                                                                                              Я вас решительно не понимаю. И кажется не только я )

                                                                                                                                                0

                                                                                                                                                Человек говорит не о ключевом слове interface, а о понятии интерфейс. Если у вас есть мутабельная переменная, но кто-то непозволяет вам ее изменить ее можно расценивать как иммутабельную.


                                                                                                                                                F# в пример. Под капотом, там все мутабельное, как завещала CLR. Иммутабелным все делает язык (интерфейс к коду).

                                                                                                                                                  0
                                                                                                                                                  В смысле не важно, как оно работает внутри? Что вы такое говорите?

                                                                                                                                                  В том и смысле, что не важно.


                                                                                                                                                  Как может быть не важным то, что по факту исполняется?

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


                                                                                                                                                  Затем, как вы можете потерять "профиты" от иммутабельности, если под капотом работает иммутабельность. Куда они денутся?

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


                                                                                                                                                  Ух. Я себе если честно даже представить себе не могу, что это.

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


                                                                                                                                                  И уж тем более не могу представить зачем.

                                                                                                                                                  За тем, что имеются все плюсы иммутабельности, и при этом работает быстро.


                                                                                                                                                  Если вместо мутации объектов вы используете новые

                                                                                                                                                  Так это под капотом где-то они новые. Вам-то какая разница? С точки зрения вашего кода они как старые.

                                                                                                                                                    0

                                                                                                                                                    Druu, я думаю вам стоит ознакомиться с документацией Redux поближе. И возможно с документацией к React.pureComputed | React.memo. На случай если вам вообще интересны эти библиотеки и вы планируете использовать их на деле.


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

                                                                                                                                                      0
                                                                                                                                                      Про «экшоны», наверное, я писал.
                                                                                                                                                    0

                                                                                                                                                    Мне всё понятно. Пишите чистую функцию, внутри которой используете мутабельные переменные, хотя бы классический императивный for (i=0; i<param.length; i++; а не функциональные map, reduce, etc. и получаете код, который снаружи выглядит как иммутабельный функциональный (чистая функция), а внутри сплошная мутабельная императивщина.

                                                                                                                                                      0

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

                                                                                                                                                        +1

                                                                                                                                                        Нет, ситуация немного другая, например есть ф-я setArrValue: (array, index, value) -> array, которая принимает массив, индекс элемента и новое значение и возвращает новый массив в котором значение по этому индексу изменено. Функция, очевидно, чистая. При этом вместо того, чтобы создавать новый массив, по факту меняется старый, но заметить мы это не можем, т.к. ссылки на старый массив не существует (это гарантируется на уровне типов). В итоге мы работаем с массивом имутабельно, но по факту все операции будут мутабельные, хоть мы и не можем заметить этот факт.

                                                                                                                                                          0

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


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

                                                                                                                                                            0
                                                                                                                                                            Ведь перед мутацией неоходимо быть уверенным в том, что на старую константу более никто не ссылается, иначе всё поломается.

                                                                                                                                                            Это гарантируется на уровне системы типов, то есть если кто-то на старую константу ссылается — то такой код просто не будет скомпилирован из-за ошибки типов.
                                                                                                                                                            Гуглите clean и linear types, если интересно.

                                                                                                                                                            –1
                                                                                                                                                            Хороший пример это утилита jq.
                                                                                                                                                            Внутри у неё все функции имеют чистый вид: значения принимаются, а новые возвращаются — но при использовании проверяется, единственная ли это ссылка, если да, то изменения производятся по месту.
                                                                                                                                                            0

                                                                                                                                                            Вопрос в том, гарантирует ли мне язык, что вся эта императивщина не вылезет наружу (как ST тот же), или нет.

                                                                                                                                                  +2
                                                                                                                                                  Толк — в интерфейсе. И в общем инварианте redux «ссылочная эквивалентность старого и нового значения равносильна отсутствию изменений».
                                                                                                                                                  0
                                                                                                                                                  Кстати, не планируют ещё для immutable splice массивов сделать такой же spread-синтаксис, как для объектов?
                                                                                                                                                  Тогда бы весь этот код выродился во что-то типа
                                                                                                                                                  const newStore = { ...store, list: [...list, 12: {...list[12], someValue: 12 } ] };
                                                                                                                                                    +1

                                                                                                                                                    Хрен редьки не слаще. Глаза от этих точек и скобок разбегаются :)

                                                                                                                                                      0
                                                                                                                                                      Даже если переносы строк оставить?
                                                                                                                                                      const newStore = {
                                                                                                                                                        ...store,
                                                                                                                                                        list: [
                                                                                                                                                          ...list,
                                                                                                                                                          12: {
                                                                                                                                                            ...list[12],
                                                                                                                                                            someValue: 12
                                                                                                                                                          }
                                                                                                                                                        ]
                                                                                                                                                      };
                                                                                                                                                      Для изменения одного единственного значения, конечно, выглядит многословно. Но если и под это будут пилить сахар, то боюсь уже диабет получить.