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

Я начал учить плюсы и 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 одновременно и параллельно друг другу. Но сейчас я так крепко прибит к своему мышлению, что не вижу и не хочу видеть плюсов динамической типизации. Они есть, но вне поля моего зрения, и все что мне остается — закрывать на них глаза, как на любые чуждые мне проявления мира, с которыми приходится жить. Понимаю, что не прав, но работать мне надо здесь и сейчас, и метаться между полюсами бюджета нет.

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

Комментарии (1038)


  1. AnutaU
    27.11.2018 16:39
    -1

    Типы и ООП связаны неразрывно.

    А чем, например, Ruby не ООПшный?


    1. Bringoff
      27.11.2018 19:55

      А куда за тот год, пока я не видел Ruby, с него убежали типы?


      1. phoenixweiss
        28.11.2018 04:12

        Никуда не убежали, но в статические они так и не превратились.


        1. Halt
          28.11.2018 08:32

          Мне кажется, статья будет неполной без этой, классической уже, картинки:

          static vs dynamic typing battles
          image


          1. OnYourLips
            28.11.2018 09:44
            -1

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

            Что касается типизации, то люблю оба типа. Динамическая очень полезна, когда надо что-то на коленке на несколько сотен строк написать.


            1. aikixd
              28.11.2018 10:25

              Имеется ввиду type inference, Когда компилятор догадывается о типах самостоятельно.


            1. BlessMaster
              28.11.2018 16:32
              +1

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


              1. VolCh
                28.11.2018 16:54

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


                1. 0xd34df00d
                  28.11.2018 17:39

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

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


                  1. VolCh
                    28.11.2018 18:38

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


                1. BlessMaster
                  29.11.2018 16:08

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


                  1. VolCh
                    29.11.2018 16:35

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


    1. codemake
      28.11.2018 14:59
      +1

      Как-то за js обидно


  1. zodchiy
    27.11.2018 16:48
    +2

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

    Знакомое чувство.


    1. HerrDirektor
      27.11.2018 17:04
      -1

      Ага, есть такое…


      1. LMSn
        28.11.2018 14:04

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


        1. HerrDirektor
          28.11.2018 16:27

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

          ЗЫ
          А начинал я вообще с изучения Си в самом начале 90х по книге Кернигана и Ритчи, которую я, будучи студентом, случайно купил в славном городе Краснодаре.
          Представляете, как я страдаю от самой мысли, что существуют ЯП с другими подходами? :) (шутка).


        1. zodchiy
          29.11.2018 13:02

          От лютого непонимания парадигмы F#, например. Очень трудно перестроить мозг с ООП на функциональщину. 12 лет с C# не проходят даром.


          1. LMSn
            29.11.2018 14:58

            Ну не надо путать теплое с мягким. Мы все же про типизацию говорим, а в F# система типов тоже строгая статическая. ООП и ФП — это из другой оперы, и, в отличие от типизации, ООП и ФП не взаимоисключающие, к нашему общему счастью. Многие недавние плюшки C# пришли после обкатки на F#.


        1. gleb_kudr
          29.11.2018 20:18
          +1

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


    1. mihacoder
      28.11.2018 17:01

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


      1. VolCh
        28.11.2018 18:40

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


        1. Neikist
          29.11.2018 09:31
          +1

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


  1. IvanNochnoy
    27.11.2018 16:54

    Да чё говорить-то. Хотели, как проще, а получилось как всегда. В PHP и Python, добавили аннотации типов. В PowerShell и TypeScript используются с самого начала. Тенденция, однако.


    1. worldmind
      27.11.2018 17:32

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


  1. alexesDev
    27.11.2018 17:13

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

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


    1. Grox
      27.11.2018 21:56
      +2

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


      1. Cidevant_Von_Goethe
        28.11.2018 14:05

        А вот скажите мне, как типы помогают в фронтенде? Как написать тип, когда одна переменная зависит от другой? Например бэкенд возвращает что то вроде:

        {
          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`». Это не решение проблемы.


        1. mayorovp
          28.11.2018 14:08

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


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


          1. Cidevant_Von_Goethe
            28.11.2018 16:01

            И сколько нужно создавать под-типов одного типа? Данное решение является самоубийством!


            1. ApeCoder
              28.11.2018 16:05
              +1

              И сколько нужно создавать под-типов одного типа?

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


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

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


            1. mayorovp
              28.11.2018 16:06

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


              1. Cidevant_Von_Goethe
                28.11.2018 18:47
                -1

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

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


                1. mayorovp
                  28.11.2018 19:04
                  +5

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


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


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


                  1. Cidevant_Von_Goethe
                    28.11.2018 19:17

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

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

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

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


                    1. mayorovp
                      28.11.2018 19:32

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


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

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


                      1. Cidevant_Von_Goethe
                        28.11.2018 19:50

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

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

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


                        1. mayorovp
                          28.11.2018 19:52

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


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


                          1. Cidevant_Von_Goethe
                            28.11.2018 20:06

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

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


                            1. mayorovp
                              28.11.2018 20:23

                              Вы можете сообщить хоть какие-нибудь подробности о своих затруднениях? Хоть один инвариант объекта из тех 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;
                              }


                              1. Cidevant_Von_Goethe
                                28.11.2018 21:22

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

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


                                1. Druu
                                  29.11.2018 02:07

                                  А это в свою очередь ведет к «types hell» — миллиону вариаций (типов) одно и того же объекта

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


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


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


        1. ApeCoder
          28.11.2018 14:19
          +1

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

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


          1. Cidevant_Von_Goethe
            28.11.2018 18:51

            Это является «кривым» решением в JS. Мой вопрос касался типов в общем. Не стоит использовать реализацию типов в JS как нечто стандартное, универсальное. Такое явно не прокатит в Хаскелле и вероятно в других языках тоже. Перестаньте смотреть на JS как на «Cвятой грааль», эталон.


            1. Chamie
              28.11.2018 19:12
              +1

              Это вообще не JS, это TS. Не надо путать.


              1. Cidevant_Von_Goethe
                28.11.2018 19:26
                -2

                Я ничего не путаю. Даже автор статьи написал: «TS — всего-лишь надмножество JavaScript.». Каким бы крутым не был язык TS — он все равно компилируется в JS. И самым слабым звеном здесь является JS. Какой бы «syntax sugar» нам не давал TS, все равно все переводится в JS.


                1. PsyHaSTe
                  28.11.2018 19:27
                  +5

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


                  1. Cidevant_Von_Goethe
                    28.11.2018 19:31

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


                    1. PsyHaSTe
                      28.11.2018 19:33
                      +1

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


                      1. Cidevant_Von_Goethe
                        28.11.2018 19:37

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

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


                        1. faiwer
                          28.11.2018 19:45
                          +1

                          Почему TS компилирует в JS а не в машинный код?

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


                          1. Cidevant_Von_Goethe
                            28.11.2018 19:52
                            -2

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


                            1. faiwer
                              28.11.2018 19:55
                              +5

                              Г — Логика.


                              1. Cidevant_Von_Goethe
                                28.11.2018 20:08
                                -3

                                Очень конструктивно. Жаль, что не могу минусовать.


                            1. PsyHaSTe
                              28.11.2018 19:56
                              +4

                              У вас странное представление.

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


                              1. Cidevant_Von_Goethe
                                28.11.2018 20:23
                                -1

                                Давайте я спрошу по другому:

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


                                1. Druu
                                  29.11.2018 02:19

                                  То, что у вас все компилируется в TypeScript, совсем не означает, что в Javascript (Runtime) все будет работать.

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


                              1. Cidevant_Von_Goethe
                                28.11.2018 20:32

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

                                Я понял вашу мысль и вероятно не совсем понятно выразил свою.


                            1. Chamie
                              28.11.2018 19:57
                              +3

                              А машинный код — это тоже слабое звено?


                        1. PsyHaSTe
                          28.11.2018 19:54

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

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


                          1. Cidevant_Von_Goethe
                            28.11.2018 20:10

                            «Неужели это возможно, Ватсон?»

                            github.com/mame/quine-relay


                1. faiwer
                  28.11.2018 19:31
                  +3

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


        1. Meloman19
          28.11.2018 14:23
          +1

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


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


          1. ApeCoder
            28.11.2018 14:32

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


            1. Meloman19
              28.11.2018 15:23

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


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


              1. ApeCoder
                28.11.2018 15:31

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

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


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


                1. Meloman19
                  28.11.2018 18:43

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

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

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


                  1. PsyHaSTe
                    28.11.2018 19:07

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


                  1. mayorovp
                    28.11.2018 19:16
                    +1

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


                  1. Druu
                    29.11.2018 02:30

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

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


              1. VolCh
                28.11.2018 15:38

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


                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 и могут быть вообще не связаны друг с другом.


                1. Cidevant_Von_Goethe
                  28.11.2018 19:29

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


              1. 0xd34df00d
                28.11.2018 17:58

                И уже в коде мы в любом случае будет проверять other_flag равен true или нет перед использованием.

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


                1. Druu
                  29.11.2018 02:35

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


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

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


                  1. 0xd34df00d
                    29.11.2018 03:15

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


                    1. mayorovp
                      29.11.2018 06:02

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


                      1. 0xd34df00d
                        29.11.2018 07:53

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


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


                    1. Druu
                      29.11.2018 06:18

                      Ну это не ЗТ.


                      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 — это, с формальной точки зрения, именно лифтанутые типы-синглтоны, а не значения.


                      1. 0xd34df00d
                        29.11.2018 08:00

                        Ну и да, тут автолифтинг, то есть значения вроде 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. Druu
                          29.11.2018 09:29
                          +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[] :)


                          1. 0xd34df00d
                            29.11.2018 15:40

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


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

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


                            1. Druu
                              29.11.2018 15:53
                              +1

                              Например?

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


          1. Cidevant_Von_Goethe
            28.11.2018 16:04

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

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

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


            1. 0xd34df00d
              28.11.2018 17:56

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


              1. Cidevant_Von_Goethe
                28.11.2018 18:33

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


        1. Ogoun
          28.11.2018 21:49

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

          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. lega
      28.11.2018 15:03

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


  1. gnaeus
    27.11.2018 17:15

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


    1. Drag13
      27.11.2018 17:41
      +4

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


    1. thekip
      27.11.2018 20:15
      +2

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


      1. gnaeus
        28.11.2018 14:56
        +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 как языка. Это проблема экосистемы в целом. Я до такого докопаюсь. Возможно, напишу свои декларации. Буду их скрупулезно поддерживать. Но придет мой коллега из лагеря динамической типизации, и скажет: "Почему я должен тратить время на все это?"


        1. IvanNochnoy
          28.11.2018 17:24

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

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

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

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


          1. gnaeus
            28.11.2018 18:20

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


    1. vintage
      27.11.2018 21:49
      +1

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


    1. Beyondtheclouds
      27.11.2018 22:37
      +1

      все там нормально у реакта с тайпингами :)


  1. aikixd
    27.11.2018 17:16

    Типы рождаются не из ключевого слова class/struct/type а из вашей головы. Когда вы думаете о задаче, вы сводите ее к набору категорий, взаимодействующих по определенным правилам. Это относится не только к программированию, это способ мышления вообще. Если мы думаем об обществе, мы не думаем о каждом человеке по отдельности. Поэтому никого не удивляет что у человека есть метод «спать», а у общества нет. Поэтому, даже используя JS я создаю категории объектов которые типами не называются лишь номинально.
    Вы начали с Шарпа и строгая статическая система типов потребовала от вас умения проводить свои мысли в порядок. Те же кто начал со скрипта и не имел врожденной склонности к порядку получили в голове кашу, причесать которую очень сложно. поэтому их код похож на доску конспиролога.


    1. aikixd
      27.11.2018 17:21

      Добавлю ещё что ООП и типизация перпендикулярные понятия. ООП получается если прикрутить код к данным (объекты носят свой код с собой). Это можно сделать на любом современном языке. Где-то проще, где-то сложнее.


      1. khim
        28.11.2018 00:10
        +2

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

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

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


      1. taujavarob
        28.11.2018 20:04
        -1

        aikixd

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

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

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


    1. eugenk
      28.11.2018 01:50
      +3

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

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


      1. aikixd
        28.11.2018 10:33

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


      1. SquareRootOfZero
        28.11.2018 10:57

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


    1. x67
      28.11.2018 14:45
      -1

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


    1. marperia
      29.11.2018 13:16

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

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


  1. nexus478
    27.11.2018 17:23

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

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

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


    1. AnutaU
      27.11.2018 17:41
      +1

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

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


    1. Aingis
      27.11.2018 18:30
      -1

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


      1. t13s
        27.11.2018 18:56
        +5

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


        1. aikixd
          27.11.2018 21:33
          +2

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


          1. eugenk
            28.11.2018 02:07

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


            1. khim
              28.11.2018 02:18

              Сильно подозреваю, что с js именно так оно и было.
              А не надо подозревать. Брендан сам рассказывает, что у него было 10 дней на всё-про-всё.

              Получился, конечно, уродец тот ещё… но могло быть и хуже…


      1. areht
        27.11.2018 21:57
        +3

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

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

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

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


        1. eugenk
          28.11.2018 02:13

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

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


          1. khim
            28.11.2018 02:20

            Поглядите на библиотеки!
            На какую стоит посмотреть, чтобы увидеть что-то новое и интересное?

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


          1. C4ET4uK
            28.11.2018 09:28

            Вы имеет ввиду библиотеки вроде left-pad?


          1. slonopotamus
            28.11.2018 10:02

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


            1. Druu
              28.11.2018 10:04
              +1

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


              1. slonopotamus
                28.11.2018 10:30

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


                1. Druu
                  28.11.2018 11:35

                  Можно пример?

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


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

                  такие дела


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

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


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

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


          1. Free_ze
            28.11.2018 11:33

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

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


            1. faiwer
              28.11.2018 11:44
              +1

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


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


              1. Cerberuser
                28.11.2018 11:53

                Не знаю насчёт «общего назначения», но, когда я увидел, как можно вывернуть наизнанку код на Ruby, вмонтированный в RPG Maker (обычный Ruby, только немного урезанный), мысли были в общем и целом схожие.


              1. Free_ze
                28.11.2018 11:55

                в JS в runtime вы можете вытворять практически всё, что угодно,… Всякие там proxy, символы, defineProperty с хитрыми полями, getter-ы и setter-ы, toPrimitiveValue|toString|valueOf и пр.

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

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

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


                1. faiwer
                  28.11.2018 12:03

                  но на языках с полноценным рефлекшном, вроде C#/Java

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


                  1. Free_ze
                    28.11.2018 12:34

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

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

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

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

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


                    1. faiwer
                      28.11.2018 12:53

                      Приведите пример задачи, которую по-вашему можно выполнить в JS, но нельзя, скажем, в C#.

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


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

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


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


                      1. Free_ze
                        28.11.2018 13:21

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

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

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

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


                        1. sshmakov
                          28.11.2018 15:08

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


                          1. Free_ze
                            28.11.2018 15:22
                            +1

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

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


                            1. sshmakov
                              28.11.2018 16:06

                              То есть нельзя. Аминь.


                              1. nightwolf_du
                                28.11.2018 16:46

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


                          1. areht
                            28.11.2018 23:46

                            > поля приватные

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

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


                    1. faiwer
                      28.11.2018 13:02

                      Вот например в эко-системе 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# получится написать такой костыль?


                      1. mayorovp
                        28.11.2018 13:35

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


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


                        1. faiwer
                          28.11.2018 13:44

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


                          1. mayorovp
                            28.11.2018 13:47

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


                            1. faiwer
                              28.11.2018 13:53

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


                              1. mayorovp
                                28.11.2018 14:06

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


                                1. faiwer
                                  28.11.2018 14:12

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


                      1. Free_ze
                        28.11.2018 14:01
                        +1

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

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


                      1. Druu
                        28.11.2018 14:21
                        -1

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

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


                        1. faiwer
                          28.11.2018 14:30

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


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


                          1. khim
                            28.11.2018 14:45

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

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

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


                            1. faiwer
                              28.11.2018 14:52

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


                          1. Druu
                            28.11.2018 14:56
                            -1

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

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


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


                            1. faiwer
                              28.11.2018 15:02
                              +1

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

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


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


                              1. Druu
                                28.11.2018 15:05

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

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


                                1. mayorovp
                                  28.11.2018 15:10
                                  +1

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


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


                                  1. Druu
                                    28.11.2018 15:13
                                    -1

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

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


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

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


                                1. faiwer
                                  28.11.2018 15:13
                                  +1

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


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


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


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


                                  1. aikixd
                                    28.11.2018 15:24
                                    -1

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


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


                                  1. Druu
                                    28.11.2018 15:31
                                    -1

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

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


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

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


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

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


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

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


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

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


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

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


                                    1. faiwer
                                      28.11.2018 15:36

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


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


                                      1. Chamie
                                        28.11.2018 15:59

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


                                  1. VolCh
                                    28.11.2018 15:46

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


                                    1. mayorovp
                                      28.11.2018 15:49

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


                                    1. Druu
                                      28.11.2018 16:01
                                      +1

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


                                      1. faiwer
                                        28.11.2018 16:13

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


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


                                        1. Druu
                                          28.11.2018 16:31

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

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


                                    1. 0xd34df00d
                                      28.11.2018 18:01

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


                        1. mayorovp
                          28.11.2018 14:40
                          +1

                          Толк — в интерфейсе. И в общем инварианте redux «ссылочная эквивалентность старого и нового значения равносильна отсутствию изменений».


                      1. Chamie
                        28.11.2018 16:15

                        Кстати, не планируют ещё для immutable splice массивов сделать такой же spread-синтаксис, как для объектов?
                        Тогда бы весь этот код выродился во что-то типа

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


                        1. faiwer
                          28.11.2018 16:18
                          +1

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


                          1. Chamie
                            28.11.2018 16:22

                            Даже если переносы строк оставить?

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


                        1. justboris
                          28.11.2018 16:26
                          -1

                          я бы вот так написал


                          const newStore = {
                            ...store,
                            list: list.map((item, index) =>
                              index === 12 ? { ...item, someValue: 12 } : item
                            )
                          };


                          1. Chamie
                            28.11.2018 19:10

                            Зачем перебирать все элементы?

                            есть же .slice()
                            list: [...list.slice(0,12), {...list[12], someValue: 12 }, ...list.slice(13)]


                    1. KostaArnorsky
                      29.11.2018 06:05

                      Приведите пример задачи, которую по-вашему можно выполнить в JS, но нельзя, скажем, в C#.
                      Все можно, можно даже сборку декомпилировать и заново собрать, если она в IL, но это адский труд по сравнению с
                      someObj.existingFunction = function() {
                          console.log("Ha-ha!");
                      }


                      1. nightwolf_du
                        29.11.2018 10:38

                        Если вам нужен дизайн с подменой функций — в c#
                        someObj.existingFunction должен иметь тип Func или Action.
                        и вы спокойно сможете подменить функцию.
                        Либо делегат (delegate ), либо event (он всё равно подтип делегата). Всё это является указателями на функции и может быть переприсвоенно, либо в случае с event — на него можно подписаться-отписаться любым своим обработчикам.

                        Хотя всё это вы и сами знаете.


                        1. VolCh
                          29.11.2018 15:02

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


                      1. areht
                        29.11.2018 10:59

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

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


                        1. KostaArnorsky
                          29.11.2018 11:50

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


                          1. areht
                            29.11.2018 15:23
                            -1

                            > JS позволяет творить невообразимые вещи.

                            Ок, записал в колонку «недостатки»


                      1. Free_ze
                        29.11.2018 11:43

                        Понятие «предка» объекта в C# и JS разное. JS-объекты ссылаются на общего предка (который может ссылаться на своего предка и далее по цепочке), в то время как в C# объекты полноценно включают в себя экземпляры объектов всех родителей (отдельно для каждого ребенка).

                        Обеспечить похожую семантику (map-объектов и прототипного наследования) — это как раз не проблема.


                        1. KostaArnorsky
                          29.11.2018 11:55

                          Если у вас это изначально запланировано, то конечно не проблема. В JS для этого ничего делать не нужно — берешь и меняешь prototype.


                          1. Free_ze
                            29.11.2018 12:07

                            Естественно) Особый класс JS-ных проблемы с сопутствующими решениями для C# сначала должны как-то появиться.
                            Моей целью было показать, что такую же семантику обеспечить можно, это не убер-фича JS, которой нигде больше нет. А то, что фактически разный код будет работать по-разному — это достаточно очевидно на мой взгляд (=


                      1. IvanNochnoy
                        29.11.2018 13:10

                        Сделать можно, если нужно
                        class SomeObject {
                            public Action ExistingFunction { get; set; }
                        }
                        var someObject = new SomeObject();
                        someObject.ExistingFunction = () => Console.WriteLine("Ha-ha!");
                        


                        1. PsyHaSTe
                          29.11.2018 15:10
                          +1

                          Что если мы хотим подменить Count у стандартного класса List<T>? Просить майкрософт поменять свойство на делегат?


                          1. IvanNochnoy
                            29.11.2018 15:50
                            -3

                            А не надо этого хотеть, подобный манки-патчинг нарушает принцип подстановки Лисков, если Вы это сделаете, то посыпятся все билиотеки, использующие List<T>. Для таких случаев есть интерфейс IList<T> делайте с ним, что хотите. Только не надо говорить, что, де, есть сторонняя библиотка, которая использует List<T> и я ничего не могу поделать. Раз автор хотел конкретный тип, то пусть его и получит. Раз MS не позволяют перегрузить Count, значит так и было задумано, ограничения как раз и нужны, чтобы нельзя было выстрелить себе в ногу.


                            1. PsyHaSTe
                              29.11.2018 15:56
                              +1

                              А не надо этого хотеть

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

                              Не говоря про то, что это используется в тонне фич того же дотнета, начиная от всяких DynamicProxy (есть такой класс, реализует все интерфейсы. в WCF активно используется), и заканчивая всякими Moq/MS.Fakes/…


                              1. IvanNochnoy
                                29.11.2018 16:23

                                Вы спросили что нельзя сделать в шарпах — вы получили ответ

                                1. Это не я спрашивал.
                                2. Я ответил на вопрос, как можно сделать запись с функциями в С#
                                3. Касаемо вопроса «Что если мы хотим подменить Count у стандартного класса List». Это ограничение .NET Framework. .NET является ООП-фреймворком и использовать там записи с функциями не приянято, вместо них используются интерфейсы. Так вот, авторы библиотеки намернно запретили изменять класс List. By design. И, да, в С# нельзя изменить то, что было запрещено. В этом весь смысл. А в JavaScript можно менять всё и везде. Если бы авторы .NET хотели бы этого, то все поля сделали бы публичными, а методы — виртуальными. Так что, да, в C# нельзя изменить, то, что запрещено, но в рамках своего собственного кода Вы свободны от ограничений.


                                1. PsyHaSTe
                                  29.11.2018 16:28

                                  Вы ответили не на тот вопрос. По сути вопрос «как расширить или подменить объект или поведение объекта, когда автор не предусмотрел такой возможности». Код на C# делает совершенно не то, что код на JS.


                                  1. faiwer
                                    29.11.2018 16:36

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


                                  1. IvanNochnoy
                                    29.11.2018 16:59

                                    Дело в том, что в JS «расширить или подменить объект или поведение объекта, когда автор не предусмотрел такой возможности» тоже невозможно! Хотите знать как? Object.freeze() и фигу Вы там чего поменяте. Разница только в том, что в JS по-умолчанию разрешено всё, что не запрещено, а .NET наоборот. А это уже вопрос дизайна, а не возможностей.


                                    1. PsyHaSTe
                                      29.11.2018 17:02

                                      В этом случае вы переопределяете Object.freeze, чтобы он проигноировал заморозку свойства, которое вы хотите подменить. Profit.


                                      1. IvanNochnoy
                                        29.11.2018 17:40

                                        Однако… Сильный ход, честно. Object.freeze, который нифига не фризит?! Мне неизвестна в С# такая простая возможность отключить базовую систему безопасности. Но это опять же by design.


                                        1. Chamie
                                          29.11.2018 18:18

                                          Фризит, но не всё.


                              1. areht
                                29.11.2018 16:47

                                > Вы спросили что нельзя сделать в шарпах — вы получили ответ.

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

                                Да, в языках разная механика внедрения изменений в поведение объектов. А ещё синтаксис разный.


                                1. PsyHaSTe
                                  29.11.2018 16:55

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


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


                                  1. areht
                                    29.11.2018 18:14

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

                                    Так у вас проблема с автором, я не с языком.

                                    Понимаете, в C# поведение List всегда неизменно. Изменение происходит на уровне подмены на другую реализацию IList. Хоть DynamicProxy, хоть черт в ступе — но это будет другой тип, с другим поведением. Поведение старого типа на уровне инстанса поменять нельзя, а просить этого некорректно. И внедряется уже не метод, а объект. Но в JS метод — тоже объект (так что это в JS получается нельзя иметь свойства, отличные от Func/Action), так что тут всё пока равнозначно. Куски объекта в JS вы тоже не можете подменять.

                                    Хак с Object.freeze — это сродни записи приватного свойства через рефлекшн.

                                    Но если у свойства тип List, а не IList, то тут уже в руки надо брать Emit… Ну или брать паяльник и идти к автору (кстати, майкрософт по делу запретил перегружать Count).

                                    Это не значит, что чего-то нельзя, это значит, что язык поощряет использование собственных парадигм (и своевременное использование паяльника). А вы хотите, вместо исправления рецепиента (как надо бы) исправлять хаком сам внедряемый класс, то есть на C# писать как на JS.

                                    А ещё в JS можно написать

                                    someObj.existingFunction = function() {};
                                    someObj.existingFunction = 6;

                                    И это тоже нельзя просто так повторить в C#, но и это тоже некорректный ответ. Формально верный, но не вызывает ничего, кроме недоумения.


                                    1. PsyHaSTe
                                      29.11.2018 18:32

                                      Так у вас проблема с автором, я не с языком.

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


                                      Понимаете, в C# поведение List всегда неизменно. Изменение происходит на уровне подмены на другую реализацию IList. Хоть DynamicProxy, хоть черт в ступе — но это будет другой тип, с другим поведением. Поведение старого типа на уровне инстанса поменять нельзя, а просить этого некорректно. И внедряется уже не метод, а объект. Но в JS метод — тоже объект (так что это в JS получается нельзя иметь свойства, отличные от Func/Action), так что тут всё пока равнозначно. Куски объекта в JS вы тоже не можете подменять.

                                      Я прекрасно понимаю. И даже согласен, мне не нравится анархия, которая дается таким образом.


                                      Тем не менее, если такое понадобилось, то JS дает больше средств.


                                      Я помню, как у нас люди работали с SharePoint, так там пришлось сборку декомпилировать и подменять в IL-коде константы, потому что майкрософт захардкодили там коннекшн стринги и имена своих таблиц, которые иначе нельзя было изменить. В итоге патчили хекс-редактором сборки, и загружали обратно, с красной пометкой "НИКОГДА НЕ ОБНОВЛЯТЬ И НЕ ЗАМЕНЯТЬ". Костыль, конечно, но вот один случай когда такое понадобилось у меня в коллекции есть.


                                      1. areht
                                        29.11.2018 19:17

                                        > Тем не менее, если такое понадобилось, то JS дает больше средств.

                                        Так их не больше, они просто доступнее.

                                        Ну то есть, основные кейсы по сложности мало отличаются, фактически только синтаксисом.

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

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


                                      1. IvanNochnoy
                                        30.11.2018 13:56
                                        +2

                                        Тем не менее, если такое понадобилось, то JS дает больше средств.

                                        Больше свободы, я б сказал. Расскажу одну историю из реальной жизни. Дебажил я как-то чужой код на PHP два часа в состоянии «то ли я дурак, то ли лыжи не едут». Наконец понял, «где собака порылась». Оказывается, сторонний модуль находил в этом коде строку регулярными выражениями (!) и патчил ее на свой лад в памяти. В результате у меня в файле один код, а в памяти выполняется другой. Стоит ли говорить, что после этого я пошел, и напился?

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

                                        Ну сейчас кто-то скажет: пишите тесты, проводите митинги, делайте документацию. А зачем? если можно просто запретить такое самовольство?

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


                          1. Meloman19
                            30.11.2018 01:14

                            В общем-то можно подменить, но с некоторыми оговорками:
                            1) С дженериками без указания конкретного типа не сработает.
                            2) Придётся лезть в unsafe.
                            3) Оптимизация может загубить всю подмену, если в одном и тоже методе вызывать смену метода у типа и использовать этот тип. Я не спец по .NET, но видимо причина во встраивании.

                            Реализация уже существует: Harmony
                            А конкретно метод DetourMethod

                            Пробный запуск
                            static void Main(string[] args)
                            {
                                MethodBase methodToReplace = typeof(List<int>)
                                    .GetProperty("Count")
                                    .GetMethod;
                                MethodBase methodToInject = typeof(Program)
                                    .GetMethod("returnNegative", 
                                        System.Reflection.BindingFlags.Static | 
                                        System.Reflection.BindingFlags.NonPublic);
                            
                                // Это тот самый метод из ссылки выше.
                                DetourMethod(methodToReplace, methodToInject);
                            
                                Check();
                            }
                            
                            static int returnNegative()
                            {
                                return -1;
                            }
                            
                            private static void Check()
                            {
                                List<int> list = new List<int>();
                            
                                // Вернёт -1.
                                Console.WriteLine(list.Count);
                            }
                            


                            1. PsyHaSTe
                              30.11.2018 01:18

                              Да, это известные схемы, в частности была отличная статья на хабре с иллюстрациями (тыц). Но это все «чит» и обход C# хаками нижележащей системы CLR.


                              1. Meloman19
                                30.11.2018 01:47

                                А как это реализовано в JS?

                                Весь «чит» тут в подмене ссылки на скомпиленный метод, если бы не защита памяти, то даже PInvoke не пришлось бы использовать и всё в пределах C# (хоть и в unsafe) можно было бы делать.


                              1. areht
                                30.11.2018 01:56

                                А вообще рефлекшн — это C# или CLR?

                                Или typeof(System.Int32) — ещё C#, а typeof(System.Int32).Attributes — уже CLR?


                                1. ApeCoder
                                  30.11.2018 06:32

                                  typeof это C# а System.Int32 это CLR :)


                                1. PsyHaSTe
                                  30.11.2018 11:49

                                  К рефлекшену есть доступ из C#, а например хак `ICorJitCompiler` нужно писать на плюсах (на шарпах не сделать вообще), и подключать в шарпы уже сборку на них.


                    1. PsyHaSTe
                      29.11.2018 12:16

                      Приведите пример задачи, которую по-вашему можно выполнить в JS, но нельзя, скажем, в C#.

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


                  1. GiAS
                    28.11.2018 14:30

                    В Java при помощи рефлексии можно не только получить информацию о классе/методе/поле, но и изменить их


                  1. Quilin
                    28.11.2018 14:38

                    Не знаю, какие вы примеры смотрели, на MSDN, например, все совершенно прозрачно — GetProperty(«DontDoIt»).GetValue(myObject);

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

                    Как сказал товарищ Free_ze если вы пользуетесь таким функционалом, вы либо совершенно точно понимаете, что вы делаете, и иначе это не сделать (Mocks-библиотеки, IoC-контейнеры, AOP-фреймворки), либо вы претендуете на свой зал славы на говнокод.ру.


                    1. faiwer
                      28.11.2018 14:40
                      -1

                      Вы точно внимательно прочитали о чём мы с Free_ze говорили? Мне кажется, что нет. Вот GiAS похоже прочитал не по диагонали и упомянул суть ? в JAVA это не readonly.


                      1. Quilin
                        28.11.2018 17:13

                        Я, конечно, напрямую не упомянул SetValue, но он там тоже есть. Если бы вы внимательно прочитали мой ответ, то увидели бы, что я привел примерами Mocks, IoC, AOP — далеко не readonly примеры. Все это в шарпе существует.

                        Если на то пошло, то рефлексия в статически типизированных языках в разы сильнее этой идеи «обратись к методу по строке его имени» — поэтому там и существуют пресловутые IoC-контейнеры и Mock-библиотеки. А в JS приходится довольствоваться spy(«myMethod») и иже с ним.


                        1. VolCh
                          28.11.2018 18:42

                          В PHP, в слабо динамически типизируемом языке, есть и IoC- контейнеры, и Mock-библиотеки.


                          1. Quilin
                            28.11.2018 19:47

                            Потому что есть рефлексия, которая появилась в PHP5-7 в рамках тенденции к переходу на типизацию и ООП.


                            1. VolCh
                              28.11.2018 20:39
                              +1

                              Типизация в PHP всегда была. Слабая динамическая. Есть тенденция к переходу на более строгую динамическую. Из 23 лет своей публичной истори 18 лет PHP живёт с ООП, с версии 4.


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


                              1. Quilin
                                29.11.2018 13:30

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

                                var x = new X()
                                x['myMethod'] = () => console.log('hi')

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

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


                                1. faiwer
                                  29.11.2018 13:36

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


                                  1. Quilin
                                    30.11.2018 10:14

                                    Слабость в том, что нельзя определить, какие аргументы принимает функция и хотя бы их имена, раз уж тип нельзя по понятным причинам. Ещё слабость в том, что надо хардкодить названия в отсутствие nameof.
                                    Неудобство в том что нельзя отличить методы от свойств без лишних телодвижений с typeof, hasOwnProperty и прочим там. Я никогда не видел, чтобы метод переопределяли в свойство, хотя это технически возможно. Да, я понимаю про функции высшего порядка, но на js в основном пишут не в фп, а в ооп стиле, где методы имеют особое значение.
                                    Я уточню, на всякий пожарный, что это конечно не финальная версия рефлексия в жиеси, но пока что она выглядит примерно как промисы без async/await синтаксиса.


                                    1. faiwer
                                      30.11.2018 10:33

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


                                      По поводу нельзя отличить "методы" (функции!) от свойств без "typeof" — а в чём проблема то? И почему typeof лишний. Ничего не понимаю. И да, функции заменят на свойства (другие виды свойств), и наоборот. Код который проверяет тип свойства перед его применением — рядовой код. Скажем какой-нибудь фильтр может быть задан строкой (привет lodash), RegExp-ом, объектом или методом. Если строкой, то по ней создаётся метод. Ну просто сплошь и рядом в JS такое. Как вы могли этого никогда не видеть. Удобно же. В нединамических языках это вроде называется "полиморфизм", а у нас утиная типизация. Она распространяется не только на параметры функций, но и на объекты с их полями.


                                      В JS методы это first-class-citizen. Т.е. просто разновидность значения. И работают с ними, соответственно точно также. Я так понимаю, вы их куда-то отдельно выносите, и хотите для них отдельного API? Ну так он в пару строк пишется.


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


                                      Ну если совсем уж придираться, то кажется нет нормальной возможности отличить async функций от обычных без обращения к toString(). А это костыль. Но этому и объяснение есть, ведь не-async функция может вести себя точно также, как и async-функция.


                                      Ещё, если придираться, то наверное, какие-нибудь грабли с Proxy есть. Но в эту сторону я не копал.


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


                                      1. KostaArnorsky
                                        30.11.2018 18:12
                                        +1

                                        if (IsValid(someArg) == false)
                                        {
                                            throw new ArgumentException(nameof(someArg), $"Invalid value.");
                                        }

                                        В JS так нельзя, что, конечно, минус, но довольно слабый аргумент. Остальные примеры мимо кассы. Действительно мощный концепт, который вряд ли в JS появиться, это Expression. Но это заменяется псевдоязыками и транспайлероми.


                                        1. faiwer
                                          30.11.2018 18:25

                                          Попробовал бегло разобраться с тем, что такое nameof в c# и только запутался. Судя по всему примерам, если написать не nameof(anything), а просто "anything", то получится тоже самое. Но я не думаю, что такой механизм кому-нибудь мог бы понадобиться. Что оно делает на самом деле? Результатом это штуки можно управлять? Можно написать nameof(a) и получить не "a"?


                                          Expression штука забавная. Но оно наверное и правда в JS не появится из-за того, что есть eval и его родной брат new Function. Правда на фоне Expression это более костыльный подход. Неспроста он зовётся eval.


                                          1. KostaArnorsky
                                            30.11.2018 19:17
                                            +2

                                            В C# повсеместно применяется авторефакторниг, и после переименования anything в something вам не нужно вручную менять везде «anything».
                                            Expression это не просто исполнение выражений, вы можете написать на C#, с проверкой типов и прочим, а на выходе получить… да хоть JS, можете склеить несколько выражений и т.д., при должной сноровке инструмент невообразимо мощный.

                                            P.S.: как вы nameof(anything), eval и т.п. форматируете?


                                            1. faiwer
                                              30.11.2018 19:19
                                              +1

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


                                            1. VolCh
                                              30.11.2018 20:47

                                              Наверное, всё же, не в С#, а в заточенных под него IDE. В каком-нибудь vim без полноценной поддержки языка вы вряд ли получите те же гарантии правильности рефакторинга, которые есть в средах, заточенных под C#, а особенно использующих его родной анализатор (не знаю, возможно ли это, но со стороны MS было бы логично использовать "кишки" компилятора C# в VS)


                                              1. mayorovp
                                                30.11.2018 20:51

                                                Это да, но компилятор все равно проверит что указанный в nameof идентификатор существует.


                        1. faiwer
                          28.11.2018 18:46

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


                    1. aikixd
                      28.11.2018 15:29

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

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


                      1. Quilin
                        28.11.2018 17:15

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


              1. IvanNochnoy
                28.11.2018 14:12

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


                1. faiwer
                  28.11.2018 14:17
                  +1

                  Вы про синтаксический сахар или про возможности языка? Что из того, что вы перечислили нельзя реализовать в коде, Кроме перегрузки операторов (про неё я как раз упомянул). Про рефлексию смешно, да. Шампунь для лысых.


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


                  1. IvanNochnoy
                    28.11.2018 15:08
                    -1

                    Сахар имеет значение. Ни один психически здровый человек не будет определять cвойство так:

                    Object.defineProperty(obj, 'key', {
                      enumerable: false,
                      configurable: false,
                      writable: false,
                      value: 'static'
                    });
                    


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


                    1. faiwer
                      28.11.2018 15:18

                      С такой формулировкой соглашусь. Я сам тот ещё "сладкоежка". Согласен, что в JS есть не все виды сахара, которые доселе были придуманы. Но за последние 5 лет их добавили столько, что спецификация языка, наверное, выросла раза в 4. И на подходе новые и новые. Уж кому-кому а JS разработчикам на острый дефицит сахара жаловаться не приходится. Я вот с нетерпением жду pipe-оператора.


    1. dimaaan
      27.11.2018 18:30

      я тоже не понимаю плюсов динамической типизации

      Ну вообщем-то есть один плюс: компиляторы/интерпретаторы для ДЯП писать гораздо легче :)


      1. eugenk
        28.11.2018 02:15

        Блин, то же самое написал! Честное пионерское, когда писал, Вашего коммента ещё не видел! :))))


    1. demonarxs1
      27.11.2018 19:08

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


      1. nexus478
        28.11.2018 00:44

        Если хочется попробовать со статикой поработать, то вам надо в первую очередь выбрать язык со статической типизацией, а учитывая ваш опыт фронтенда — то попробуйте написать десктоп или мобильное приложение, заодно посмотрите как с помощью статически типизированных языков разрабатывают интерфейсы. Например, можете взять C# + WPF.

        Из книг можете взять следующие:
        1. Грэди Буч «Объектно-ориентированный анализ и проектирование с примерами приложений».
        2. Роберт Мартин «Принципы, паттерны и методики гибкой разработки на языке C#» — там описываются принципы SOLID, которые очень важны в ООП (особенно Inversion of Control, IoC).
        3. Сергей Тепляков «Паттерны проектирования на платформе .NET» — описание классических паттернов банды четырех с использованием C# + SOLID. На мой взгляд, эта книга написана поинтереснее, чем у GoF.

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


    1. max1gu
      27.11.2018 20:59
      +4

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


      1. gBear
        27.11.2018 21:56
        +2

        Серьезно?! Какой-нибудь erlang смотрит на вас с легким недоумением :-)


        1. khim
          28.11.2018 00:14
          +2

          А откуда он на нас смотрит? Из какого проекта? Просто интересно…


          1. wlr398
            28.11.2018 03:10
            -1

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


            1. khim
              28.11.2018 03:23
              -1

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


              1. wlr398
                28.11.2018 03:39
                -1

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


                1. Goodkat
                  28.11.2018 15:27

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


              1. Goodkat
                28.11.2018 10:07

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


                1. x67
                  28.11.2018 14:55

                  <сарказм> Оу, то есть это не ограничение эрланга?? </сарказм>


              1. Grox
                28.11.2018 14:48

                Есть большая разница между «не может» и «не хочет».


                1. khim
                  28.11.2018 17:07

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


                  1. mayorovp
                    28.11.2018 17:08

                    Но язык тут ни при чем.


                  1. Grox
                    28.11.2018 17:20

                    с программистами плохо
                    И тогда причём тут программисты?


                    1. khim
                      30.11.2018 14:32

                      А это это всё сотворил? Марсиане?


          1. gBear
            28.11.2018 09:45

            Про телеком вам уже намекнули.
            Из того, что «ближе»… rabbitMQ или какой-нибудь couchDB, например.


          1. gatoazul
            28.11.2018 11:26

            RabbitMQ


      1. gatoazul
        27.11.2018 23:07
        -4

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


        1. ser-mk
          28.11.2018 00:24
          +3

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


          1. gatoazul
            28.11.2018 11:27

            Например, поиск плагиата. Я решал его, когда это еще не было мейнстримом.


          1. PsyHaSTe
            28.11.2018 15:36

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


        1. eugenk
          28.11.2018 02:21

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

          Честно признаюсь, ни разу не замечал. Хотя занимаюсь подобными достаточно часто.


        1. San_tit
          28.11.2018 07:27

          А по моему опыту, такие задачи как раз таки решать проще с статической типизацией: набросал тип, а дальше с ним работаешь. Если нужно добавить обработку — добавил, нужно новое Поле — добавил. Никакого геморроя из разряда "договориться с коллегой об интерфейсах", все отлично читается из кода (аннотации и комментарии — костыли. Тип — самодостаточен).


          1. gatoazul
            28.11.2018 11:28
            -2

            Набросал тип. Обвесил типизацией. Не работает. Выкидываешь и начинаешь все заново.


            1. mayorovp
              28.11.2018 11:36
              +1

              У вас странные представления о том как придумываются типы данных.


        1. kagetoki
          28.11.2018 09:47
          +2

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

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


          1. gatoazul
            28.11.2018 11:28
            -3

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

            И так раз пять.


            1. mayorovp
              28.11.2018 11:36
              +1

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


              1. gatoazul
                28.11.2018 13:02
                -3

                У вас странные представления о моих представлениях


              1. rjhdby
                28.11.2018 17:54

                Похоже тут обычная несогласованность терминологии. Насколько я понимаю, речь про некую исследовательскую работу.
                Т.е., утрировано, дано «Абляция», а в результате надо получить «Прецессия».
                1. Придумываем «как решать»
                2. Реализуем
                3. Что-то не устраивает — скорость, качество, потребление памяти, удобство использования, косяки с граничными случаями, вообще не работает, etc.
                4. goto 1

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


            1. kagetoki
              28.11.2018 11:43
              +1

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

              О каких издержках кстати речь? Что код тормозит? Или процесс разработки слишком долгий? Или памяти много потребляется?


              1. gatoazul
                28.11.2018 13:07
                -2

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

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

                Чем вам поможет типизация?

                Введете тип Text и его метод Process?

                Введете типы String и Word? А нужны ли они? И если нужны, то как должны быть реализованы? А если реализация оказалась неудачной, но просто заменить ее внутренности, не затронув верхние слои, не получается?


                1. 0xf0a00
                  28.11.2018 13:38

                  Введу, я еще и тип «предложение» введу, который будет массивом указателей на свои «слова». :)
                  Типизация мне поможет выстроить иерархию объектов, написать короткие и безопасные методы, поможет избежать мне ошибки «не туда».
                  А еще как очень правильно написали выше — типизация структурирует и приводит в порядок мысли.


                1. Druu
                  28.11.2018 13:53
                  +1

                  Статические аннотации требуют лишней работы.

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


          1. eugenk
            28.11.2018 13:32

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

            Таварисчь наверно имел в виду ситуацию, когда задача сильно изменчива. Тогда возможно и да. Типонезависимые синтаксические формы высокого уровня возможно лучше будут выглядеть на чём-то динамическом. Хотя я правда для для подобной задачи (моделирование алгоритма БПФ Кули-Тюки с целью его конвейерезации и запихивания в FPGA) успешно юзал С++. Кстати это был вообще первый случай, когда добровольно и сознательно заюзал классы, до того писал в чисто процедурном стиле. А ведь есть ещё современные языки с выводом типов, такие как Rust! На чем-то подобном вообще всё должно ложиться мягко и пушисто…


      1. eugenk
        28.11.2018 02:19

        Динамическая типизация несет социальныую функцию — привлечение к программированию непрограммистов.

        Уже несколько раз здесь писал, повторю ещё раз. Готов был бы в это поверить, если бы не сложность библиотек, написанных на js, питоне и прочем динамическом. Это говорит о том, что скорее мы, сторонники статической типизации чего-то не понимаем.


        1. khim
          28.11.2018 02:31

          Посмотрите лучше не на сложность библиотек, а на сложность решаемых с их помощью задач. Ну вот, хотя бы, пресловутый новый дизайн GMail: огромное количество кода, «сложнейшие» библиотеки… и что в результате?

          Вот чего всё эта бурная деятельность породила? Более скруглённые уголки? Сравните, хотя бы, с Eclipse или Intellij Idea… ну или функциональность Google Docs сравните с MS Office 95 (и то и то — 12 лет с первой версии)?


          1. eugenk
            28.11.2018 02:58

            Ну вот, хотя бы, пресловутый новый дизайн GMail: огромное количество кода, «сложнейшие» библиотеки… и что в результате?

            Ээээ батенька… Это Вы ещё яндекс-почты не видели! Вот где жопа так жопа… У меня почта на яндексе с 2004-го года, постепенно дозреваю, чтобы от неё отказаться. Ноут хоть и не самый современный, но и далеко не слабый. Тормозит так, что материться хочется! Впрочем думается это скорее проблема не js, а говнокодеров. И скорее всего она одна и та же что для гугла, что для яндекса. Смешно. Помню как повсеместно отказывались от флеша (мне тогда было очень грустно ибо AS3 я очень любил и часто юзал как язык моделирования перед реализацией на С или в FPGA). Одной из претензий было что говнокодеры заполонили все тырнеты своими говнобаннерами, и в результате нормальному юзеру стало невозможно серфить. Ну хорошо. Отказались. Сейчас та же жопа, но только с js. Проблема в ЧЕЛОВЕКЕ, а не в технологии…

            Вот чего всё эта бурная деятельность породила? Более скруглённые уголки? Сравните, хотя бы, с Eclipse или Intellij Idea… ну или функциональность Google Docs сравните с MS Office 95 (и то и то — 12 лет с первой версии)?

            Абсолютно согласен. Но тут проблема того же порядка. Когда из-за какой-то мало кому нужной фичи прикладывают титанические усилия, и в результате как Офис 95 тормозил на 486DX, так теперь Google Docs тормозит на i5. Нет, вру. Сейчас всё круто! Ибо тормозит в онлайне, в броузере! Прогресс твою мать! :)))
            А вообще мне кажется человечеству давным-давно пора несколько притормозить с онлайном, гаджетами и прочей информационщиной. И заняться чем-то скучным и неинтересным. Например колонизацией океана. Или космоса. Иначе боюсь добром это не кончится. Живём всё-таки в реальном мире…


            1. Free_ze
              28.11.2018 11:46

              это скорее проблема не js, а говнокодеров. И скорее всего она одна и та же что для гугла, что для яндекса

              Парочка из крупнейших IT-компаний не могут найти «не говнокодеров», но к JS это не имеет отношения, конечно же.


              1. faiwer
                28.11.2018 11:49
                +1

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


                1. khim
                  28.11.2018 17:21

                  Пожалуй JS к этому отношения и правда не имеет.
                  На первый взгляд — нет. А вот на второй… Если посмотреть на библиотеки для «классических» языков (ну там Qt или даже, прости госсподи, MFC или Swing) — то там не происходит ежегодных «революций», заставляющих всё переделывать в 100500й раз… а программы пишутся и правятся… и есть ощущение, что больша?я (если не бо?льшая) часть всей этой деятельности вокруг JavaScript — как раз рождена из тех же самых побуждений, что и тормозной интрефейс GMail…


                  1. faiwer
                    28.11.2018 18:52
                    +1

                    Ну так и в ruby\python\php нет ежегодной революции. Дело не в дин. типизации. Это на JS навалилось так много народу и новых задач за относительно короткое время. По сути была пустыня с кактусом, пришёл прораб и сказал — здесь должен быть город на 40 миллионов человек, роща и огромное озеро, и заверте… Как тут без революций.


              1. eugenk
                28.11.2018 13:50

                Парочка из крупнейших IT-компаний не могут найти «не говнокодеров», но к JS это не имеет отношения, конечно же.

                Вам уже ответили, причем на мой тупой и неоригинальный дилетантский взгляд — очень логично. Добавлю от себя. В веб-дизайне (что клиентском что серверном) на мой взгляд процент говнокодеров вообще сильно выше среднего по IT. Только это особенность не js или php, а рынка труда. Просто вакансий по вебу приходится наверно сотня на одну про мои любимые FPGA. И куда идёт человек, полгода назад решивший стать программистом? Правильно! В веб-прогерство. Потому куча народу постоянно пробует в этом свои силы. В том числе народа совершенно левого, не пригодного для программирования вообще. С другой стороны народ, почувствовавший к IT вкус, из веба вобщем-то рассасывается. Ибо задачи там обычно не сильно творческие. Я например люблю веб-программирование только из-за его «отзывчивости». Я могу сделать проект, который увидят миллионы людей и я могу вступить с ними в какие-то отношения. Но всё это только потому, что пишу я исключительно для себя (ну хорошо, признаюсь, что собираюсь кое-что из своих поделок продавать). Работать же предпочитаю в совершенно другой области.


                1. Free_ze
                  28.11.2018 14:13

                  Эти компании имеют достаточно ресурсов, чтобы отсеивать кандидатов при малейших сомнениях, поэтому я не думаю, что проблема избытка проходимцев там стоит настолько остро.
                  Другое дело, что JS своим дизайном не способствует росту скилла «рабочей командной лошадки». Получаются именно «ниндзи», творцы с полетом мысли, когда код пишется быстрее, чем обдумывается ТЗ (как тут уже неплохо подметили), которым путь в другие области и языки уже практически заказан (ибо слишком много шаблонов придется рвать, а тут уже неплохо кормят).


          1. kozar
            28.11.2018 16:52

            В Google Docs реализовано одновременное редактирование документов несколькими пользователями в почти реальном времени по сети.
            Ничего подобного не было ни в Office 95, ни даже 2007. По-моему, сложность реализации этого не меньше, чем форматирования ячеек в таблице.


            1. khim
              28.11.2018 17:39

              Проблема в том, что эта фича как раз была реализована ещё 12 лет назад — и сделали её за полгода (или, может, даже меньше). И за прошедшее время весь «прогресс» не привёл ни к появлению стилей, ни к поддержке 3D-чартов, ни к чему-либо ещё из полезных фич, про которые бывшие пользователи MS Office спрашивают.

              Зато не то два, не то три раза изменили весь дизайн и переписали код с нуля, неизмеримое число раз «игрались с цветами» и прочее.


              1. kozar
                28.11.2018 19:36

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


          1. diseaz
            28.11.2018 17:44

            Так Gmail-то как раз — про статическую типизацию. Исходно там были всякие Java+GWT и Closure со включённой строгой типизацией при компиляции. Финальный JS, который грузится на клиент — машиногенерённый, как байткод Java, который, к слову, тоже не особо statically typed со своими дженериками через object.


            1. Free_ze
              28.11.2018 17:49

              байткод Java, который, к слову, тоже не особо statically typed со своими дженериками через object

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


              1. diseaz
                28.11.2018 18:24

                Вытащить информацию о типе можно и в питоне, например. Питон — строго типизииованный, но не статически. К слову, в стиле duck typing можно херачить на, наверное, любом языке с достаточно мощным reflection'ом, например на Go.


              1. PsyHaSTe
                28.11.2018 18:36

                Ничего вы не вытащите.


                Причем, все настолько плохо, что по сути вы можете с этим столкнуться и во время компиляции (А не только в рантайме):


                private interface IFoo<T>
                {
                
                }
                
                private class A {}
                private class B {}
                
                private class Foo implements IFoo<A>, IFoo<B>  {
                }


            1. khim
              28.11.2018 17:58
              +1

              Я сейчас беру GMail не в качестве «динамического языка», а в качестве «бурного развития JS». Потому что бурную деятельность я вижу. А вот развития — нет. Есть попытка как-то решить проблему изначально ужасного дизайна наваливаением многих уровней костылей поверх всего того, что уже было порождено за долгие годы. Причём в результате ничего подобному тому, что призошло при переходе, скажем, Turbo Pascal CRT => Turbo Pascal Turbo Vision => Delphi (когда на каждом этапе количество библиотек возрастало, но зато количество кода, который нужно писать руками для получения сравнимого результата падало) я не наблюдаю.

              Наоборот — чем дальше в лес, тем более сложными становятся решения для тех же задач.

              По мне — так это скорее регресс, а не прогресс…


              1. diseaz
                28.11.2018 18:32

                Значит Gmail — плохой пример в данном конкретном случае. Ибо бурная деятельность тут происходит не на уровне JS, а как раз на тех уровнях, где статическая типизация. И JS мог развиться до любых высот, но есть груз легаси в инструментах компиляции и потребность запускаться на legacy-браузерах.


              1. faiwer
                28.11.2018 19:29
                +2

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

                Ну дык кол-во кода для написания одного и того же на портянке jquery+велосипеды или, скажем, backbone+any_template_engine радикально упало. В разы. А сам код стал проще. Местами даже с элементами FP.


                Наоборот — чем дальше в лес, тем более сложными становятся решения для тех же задач.

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


                Или вы всецело имеете ввиду? Ну я думаю со времён изобретения MVVP и vDom люди только и делают, что экспериментируют с уже известными подходами и технологиями, пытаясь найти что-то оптимальное. Скажем команда React в одном из новых релизов обещает COMMIT (прямая работа с DOM) фазу разбить на подзадачи с использованием iddleCallbacks. Что в этом плохого?


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


                Не понимаю вашего пессимизма. Есть кривой и косой gmail. Это следствие менеджмента в Google. Можно его же сделать быстрым как метеор. Благо технологии позволяют. Подходы все известны. Никакой магии нетребуется. Но надо чтобы кто-то взялся за это. Но ведь премию и повышения дают только за редизайны.


                1. PsyHaSTe
                  28.11.2018 19:32

                  Меня например лично печалит, что когда мне поставили задачу "определить, есть ли на страницу google pixel", мне пришлось селениум в проект затаскивать, просто чтобы понять, есть ли некоторая подстрока на странице. А все потому, что я не могу просто сделать GET урла и поискать текст, ведь всё, что я там найду это <html><body><script src='app.js' /></body></html>.


                  1. faiwer
                    28.11.2018 19:47

                    Ну так себе проблема, если честно. Вот поставят вам задачу, определить используется ли библиотека X в бинарном приложении Y, там никакой Selenium вам уже не поможет. Хорошо если исходник найдёте. Ну или есть навыки джедая в дизасемблерах. А если нет? :)


                    1. PsyHaSTe
                      28.11.2018 19:58

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

                      Ну так себе проблема, если честно

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


                      1. faiwer
                        28.11.2018 20:02

                        Семантика HTTP как по мне поломана на прочь

                        Да её вообще уже к лешему послали. Как и почти все остальные устои. Тут где-то что-то мелькало, что мол Google от URL хочет отказаться, а вы про гипертекстовый фидоинтернет :)


                      1. int03e
                        28.11.2018 20:07
                        +1

                        Веб перестал быть «набором html файлов», и это естественная эволюция. Семантика нифига не поломана, сделайте GET/POST запрос к любому API и получите предсказуемый результат в формате JSON/XML.


                        1. PsyHaSTe
                          28.11.2018 21:15

                          Да, но любой запрос (до недавнего времени) содержал полезные данные.


                          Теперь же запрос страницы отдает заглушку-лоадер, который и будет загружать непосредственно страницу.




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


                          1. VolCh
                            29.11.2018 15:21

                            Это не техническая заглушка типа редиректа, это полноценный html


                            1. PsyHaSTe
                              29.11.2018 15:57
                              +1

                              Да нет, заглушка и есть. Это не мешает ей быть валидным HTML-файлом, но контента в ней нет.


                              1. VolCh
                                29.11.2018 16:39
                                -1

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


                      1. VolCh
                        28.11.2018 21:02

                        Кое-где она поломана, да, но общий подход по GET / получить html-страницу-заглушку не ломает семантику. Это вполне себе ресурс — A network data object or service that can be identified by a URI. Про какой-то определ'нній вид контента речи нет.


                      1. 0xd34df00d
                        28.11.2018 22:35

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

                        Особенно если оно заинлайнится в каких-нибудь там плюсах.


                        1. PsyHaSTe
                          28.11.2018 23:16

                          Как подгрузить библиотеку, не зная её имени? Это было бы странно.


                          1. 0xd34df00d
                            28.11.2018 23:30

                            Я что-то потерял нить.


                            Зачем её подгружать в рантайме? Может, в компилтайме #includeнули хедер?


                            1. PsyHaSTe
                              28.11.2018 23:45

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


                        1. creker
                          29.11.2018 01:19

                          Сравнение графов функций. Часто есть строки. Ну и инлайнится довольно редко. В общем, решаемо, если только использование библиотеки не ограничивается каким-нить leftpad из огромного фреймворка.


      1. DelphiCowboy
        28.11.2018 07:49
        -2

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

        А потом в результате в соседней теме спрашивают почему софт тормозит? habr.com/post/431282

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

        .

        .

        .

        .

        .

        .

        .

        .

        .

        .

        .

        Ну вот, хотя бы, пресловутый новый дизайн GMail: огромное количество кода, «сложнейшие» библиотеки… и что в результате?

        Вот чего всё эта бурная деятельность породила?

        Тормоза и отжирание памяти конечно породила!


        1. eugenk
          28.11.2018 08:34

          Тормоза и отжирание памяти конечно породила!

          Ещё раз. Это не проблема js. Это проблема с одной стороны тупых говнокодеров, не думающих об оптимизации от слова совсем. С другой стороны текучки у фирм-разработчиков, когда кто-то начинает работать, потом увольняется оставив кучу говнокода, в котором толком никто не может разобраться. Затем приходит другой, нарастив сверху один слой точно такого же говнокода. Затем третий и т.д. В итоге несложная веб-страничка превращается в море жидкого дерьма. Которую по-хорошему надо бы переписать с нуля, но во-первых лень, во вторых жаба душит. В третьих это проблема человечества, слишком увлёкшегося IT и тратящего на это кучу усилий, абсолютно не соответствующую полезности этой деятельности. А когда коту нечем заняться, он как известно яйца лижет. Что мы и наблюдаем.
          P.S. Минуснул не я. Честное пионерское! :)


          1. impwx
            28.11.2018 14:13

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

            Вспомните, как появились V8, HipHop, KPHP и другие подобные проекты. Когда язык поощряет пресловутую «свободу самовыражения», писать костыльный код на нем становится легко и приятно. Говнокодеры радуются тому, что им теперь удобно работать, и вкладывают миллионы человеко-часов в решение прикладных задач. В итоге у бизнеса есть система, которая худо-бедно выполняет свою функцию, но переписать ее как следует уже экономически невозможно, и приходится изобретать умопомрачительно сложные способы оптимизации.


          1. Crandel
            28.11.2018 16:15
            +1

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


            1. Aingis
              28.11.2018 17:34

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


              1. impwx
                28.11.2018 18:00

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


                1. PsyHaSTe
                  28.11.2018 18:39
                  +1

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


                  1. 0xd34df00d
                    28.11.2018 22:35

                    Настоящий C++-программист должен держать весь C++ в голове, а это, к сожалению, куда сложнее :(


                1. Aingis
                  29.11.2018 10:18

                  А меньше, чем держать весь проект в голове, для вас уже не дисциплина что-ли?


                  1. impwx
                    29.11.2018 10:22
                    +2

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


            1. Chamie
              28.11.2018 19:21

              «Проблема в JS» ? «проблема JS».
              HTML тоже называли недостаточно строгим и хотели перейти на строгий XML. Не взлетело. С JS, думаю, если бы его попытались сделать «дисциплинирующим», вышло бы то же самое.


              1. PsyHaSTe
                28.11.2018 19:26

                Жалко, что не взлетело.


                «Проблема в JS» ? «проблема JS».

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


                1. Chamie
                  28.11.2018 20:04

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


                  1. PsyHaSTe
                    28.11.2018 21:15

                    Правильно.


                    Под кучей мы имеем в виду большинство, конечно же.


    1. KvanTTT
      27.11.2018 22:53

      Это должет быть не одномерный, а двухмерный: динамическая — статическая, слабая — строгая. И тогда JS будет в самом углу. Да и почему все заканчивается Хаскелем? Есть еще более строгие языки типа Idris, Agda.


      1. nexus478
        28.11.2018 00:48

        Потому что в том докладе были взяты топ 20 языков (учитывались такие рейтинги как TIOBE, PYPL, RedMonk (stackoverflow + gihub)). То есть это языки, которые наиболее часто обсуждают и используют.

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


    1. eugenk
      28.11.2018 01:59

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

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


      1. khim
        28.11.2018 02:37

        Уж больно нетривиальные вещи пишут на том же js.
        А какие именно? Нет, мне правда интересно.

        Я вижу огромное количество бурной деятельности и огромное количество… не хочу ругаться матом… но вот чего-то, где я мог бы понять — почему оно такое большое и сложное… не вижу. Вижу поделки, похожие на то, что в прошлом веке делались с помощью Visual Basic или Delphi — только с примерно в 100 раз большими требованиями к ресурсам и со в 100 раз большим количеством кода.


        1. eugenk
          28.11.2018 03:19

          А какие именно? Нет, мне правда интересно.

          Признаюсь абсолютно честно. Я тут особо не специалист. Я таварисчь в основном очень и очень низкоуровневый. FPGA, микроконтроллеры, реальное время и т.п. Сходу так приходит из того чем пользовался и что дико понравилось (а пожалуй даже где-то как-то удивило) это jsplumb (рисование графов) и d3.js (визуализация графиков и диаграмм).

          Вижу поделки, похожие на то, что в прошлом веке делались с помощью Visual Basic или Delphi — только с примерно в 100 раз большими требованиями к ресурсам и со в 100 раз большим количеством кода.

          Вот с этим остаётся только согласиться. Сейчас ваяю потихоньку некий чисто свой проектец. Пишу на ts. Так из всех библиотек у меня там только require.js и то только потому, что без неё непонятно как грузить то безобразие, которое ts собирает в единый файл. Даже jquery не использую. И rich text editor у меня свой (он довольно специфический). Глянул в своё время на angular, это просто ужос, ужос, ужос. Дерьмовина типа hello world тащит два с половиной мегабайта всякого говна! У меня весь проект (на данный момент) меньше 200 килобайт! Так что да, ощущение того что всё сделано через жопу, самое что ни на есть явственное. Но повторяю ещё раз, я человек из совершенно другой области. За веб-программированием наблюдаю с интересом и даже кое что пишу. Но оценить профессионально не в состоянии, для этого в этом надо жить. Но на мой тупой дилетантский взгляд со стороны, развивается всё очень и очень бурно. Хотя согласен, качество порой сильно оставляет желать…


          1. defaultvoice
            28.11.2018 10:50

            Так из всех библиотек у меня там только require.js и то только потому, что без неё непонятно как грузить то безобразие, которое ts собирает в единый файл.

            Подозреваю, что так же, как и в обычном js – динамические импорты. Судя по доке ts они ещё в 2.4 появились.


            1. eugenk
              28.11.2018 13:58

              Честно говоря не знаю как это называется, в конфиге задается генерация в виде atm-модулей. Раньше ts вообще всё лил в один файл по умолчанию, а теперь вон чтобы слилось в один и оттуда стартовало, приходится немного постучать в бубен. Давно на ts ничего не писал, с версии 1.8. А тут сразу третья…


              1. mayorovp
                28.11.2018 14:11

                А куда слияние в один файл пропало-то? Просто указываете outFile вместо outDir и наслаждаетесь жизнью же...


                1. eugenk
                  29.11.2018 07:55

                  Он для этого требует тип модуля AMD и в таком виде и льёт. Простым способом стартануть оттуда при загрузке, у меня не получилось. Хотя честно говоря особо и не разбирался. Работает через require.js и ладно. Благо сама require.js всего 17 килобайт.


                  1. mayorovp
                    29.11.2018 08:27

                    Хм, и правда. Но формат там довольно простой, можно было и без require.js обойтись


          1. vintage
            29.11.2018 09:21

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


      1. KostaArnorsky
        28.11.2018 07:38

        Она очень удобна для быстро меняющегося фронта. Не для хардкорной бизнес-логики, которая в множестве переехала в браузер, а для UI, маппинга, viewmodel, раутинга, анимаций — все равно это все как правило долго не живет и постоянно переписывается, статическая типизация там только обуза и дополнительный источник мерж-конфликтов меняющегося boilerplate кода.
        Я сам .NET дев, но при всей любви к статический сильной типизации на бэке, до появления анонимных типов и dynamic С# временами подбешивал необходимостью в очередной раз менять одноразовую фигню потому что-то переехало в другое место на странице, и количеством DTO.


        1. eugenk
          28.11.2018 08:10
          +1

          С одной стороны да, согласен. С другой несколько спорно. У меня пожалуй было два проекта, такого типа, о которых Вы говорите. Первый — некая хитрая плата сбора данных на FPGA. Чтобы не паять туда микроконтроллер, управляющий процессор я реализовал прямо в той же FPGA. Следующим шагом было написать для всего этого безобразия ассемблер. Но поскольку задача постоянно менялась и система команд под неё постоянно оптимизировалась, ассемблер я сделал таблично управляемым. Просто в некий файл в специальном формате забивалась текущая система команд и на основе неё компилился код.
          Второй случай — когда я тяжело болел форексом, писал некую софтину для финансовой аналитики. Серверная часть писалась на С++ в виде некоего ядра и набора плагинов. А отображалось всё в броузере. Проблема была точно такая же, как в первом проекте с FPGA. Я не знал заранее, что и как я в следующий момент захочу изобразить. И решена была точно так же. Вместе с данными в броузер отправлялся некий заголовок в текстовом формате, их описывающий. И всё работало хотя и не на пять с плюсом, но на твердую четверку.
          Мораль сей басни такова. В любых, самых изменчивых проектах, всегда есть некоторое относительно стабильное ядро. Скорее всего не совсем простое. Управляемое некими командами. Вот это-то ядро и имеет смысл реализовывать тщательно, не скупясь, на чем-то хорошо помогающем отлавливать ошибки (в частности статически типизированном). А все изменения выносить в некий управляющий этим ядром простой язык. Думаю (хотя могу ошибаться, ибо не в точности знаю Ваших задач) на Вашем месте я точно так бы и поступил. Хотя Вам конечно виднее, на истину я никак не претендую.


        1. areht
          28.11.2018 20:52

          Анонимные типы — это статические типы. А зачем dynamic в UI?


          1. KostaArnorsky
            29.11.2018 06:40

            Но анонимные типы их не нужно описывать.

            А зачем dynamic в UI?
            Например
                return View(new
                {
                    User = user,
                    Upjachki = upjachki
                });
            
            ...
            
            @model dynamic
            
            @foreach (var upjachka in Model.Upjachki)
            {
            }
            

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


            1. PsyHaSTe
              29.11.2018 12:43

              Option<Upjachki> чем не подходит?


              Зато не будет проблем с Model.Upjacki.


              Динамики вообще не нужны. В языки испокон веков был Hashtable (позже Dictionary), который делает все, что нужно. Никаких проблем вместо foo.a.b написать foo["a"]["b"]. Зато исчезает куча проблем как с производительностью, так и с неявностью, что есть у объекта, что нет, где упасть может, а где нет.


              1. KostaArnorsky
                29.11.2018 19:47

                Тем, что его нужно описать. Когда у вас сотни страниц и одноразовых DTO, начинает напрягать. Зачем Option, кстати? Явность foo[«a»] ничем не лучше foo.a, так же замечательно падает.


                1. PsyHaSTe
                  29.11.2018 20:18

                  Тем, что его нужно описать.


                  И хорошо. Описано в одном месте, все инварианты проверены только на границе. Дальше можно безопасно пользоваться. Слышали про защитное программирование? Удобная концепция.

                  Когда у вас сотни страниц и одноразовых DTO, начинает напрягать. Зачем Option, кстати?

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

                  Явность foo[«a»] ничем не лучше foo.a, так же замечательно падает.

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


                  1. KostaArnorsky
                    29.11.2018 20:50

                    Я в целом с вами согласен, но весь этот boilerplate…
                    Хорошо бы иметь что-то типа @model returned by Foo.Bar, тип то фактически описан.


                    1. PsyHaSTe
                      29.11.2018 22:45

                      Ну вон я на F# пример привел, как раз то, что нужно, по идее.


                    1. Druu
                      30.11.2018 04:19

                      @model returned by Foo.Bar, тип то фактически описан.

                      TS так умеет.


      1. VolCh
        28.11.2018 12:14

        Часто достаточно посмотреть на TS описание типов для простой вроде JS функции и преимущества будут видны.


    1. DRVTiny
      28.11.2018 11:06
      +1

      Язык Си создан в 70-е — и в нём нет никакого ООП.
      Язык QuickBASIC (компилятор, не путать с интерпретатором QBASIC), созданный в середине 80-х, позволяет одной директивой заставить разработчика декларировать типы всех переменных, и там тоже нет ООП.
      При этом современный статически типизированный язык Crystal, например, который по задумке тотально ООП, не только в коде самого компилятора очень часто процедурный, но и позволяет без проблем писать в процедурном стиле.
      При этом z = x + y в большинстве статически типизированных языков запросто приводит к неверному результату, и упомянутый тут язык Java долгое время таскал внутри себя баг в алгоритме бинарного поиска, когда результатом m = (l + r)/2 было число намного меньше l. Ибо Int склонен переполняться (впрочем, как и float, там просто разрядности мантиссы становится недостаточно), а проверить это разработчикам компиляторов не досуг.
      При этом многие интерпретаторы динамически изменяют разрядность переменной при переполнении.
      Я, собственно, к тому, что в статически типизированных языках нет ничего «современного», они появились вообще-то раньше динамических, в статически типизированных языках совершенно не обязательно использовать ООП (и совершенно не факт, что в динамически типизированном языке можно не использовать ООП), ну и да, в конечном итоге мне совершенно непонятно, причём тут вообще проектирование классов и статическая типизация.
      P.S. И да, если убрать колоссальный пласт «неправильного» кода без ООП на том же языке Си — боюсь, что автору не пришлось бы написать свой пост, ибо нечем и некуда.


      1. gatoazul
        28.11.2018 11:32
        +1

        Добавлю, что Smalltalk — отец ООП — имел динамическую типизацию. Статическая типизация ООП а ля С++ — это более поздний костыль, призванный увеличить производительность за счет переноса связывания на время компиляции.


    1. VolCh
      28.11.2018 12:10

      Картинка не полна без разделения ещё и на строгую, и слабую.


      1. nexus478
        28.11.2018 13:23

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


  1. dipiash
    27.11.2018 17:42

    К сожалению, данные API в runtime TS мне никак без валидатора не поможет прочекать (в отличие от тех же нормальных статических языков). Консистентность кода в TS? ровно до того момента когда надоест бороться с быстро развивающимися библиотеками (а тайпинги для них отстают), придет манагер и скажет: «что-то долго» и т.п. => вы свалитесь к any.
    Так что, давайте оставим статическое статическому, а динамическое динамическому и не будем пытаться сделать из одного другое и наоборот


    1. Grox
      27.11.2018 22:04

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


    1. Druu
      28.11.2018 04:23

      бороться с быстро развивающимися библиотеками (а тайпинги для них отстают)

      Сейчас все больше и больше "быстро развивающихся библиотек" пишут сразу на ts.


  1. evocatus
    27.11.2018 18:24
    +4

    Тот способ мышления, который вы описываете, скорее можно назвать «data driven» и «bottom up», это не связано напрямую с типами.
    Вот, например, отличная презентация с демонстрацией такого подхода к дизайну (что забавно — тоже на примере игры), притом человек пишет на Clojure — языке с динамической типизацией (но строгой иммутабельностью).


  1. impwx
    27.11.2018 18:42

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


    1. evocatus
      27.11.2018 18:52

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


      1. impwx
        27.11.2018 22:01
        +1

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


    1. VolCh
      28.11.2018 12:18
      +1

      Ну как нет? Нет необходимости описывать все типы. Гибкие системы типов обычно позволяют описать что угодно, но вот это описание может быть очень и очень громоздким, по сути write only.


      1. impwx
        28.11.2018 13:15
        +1

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

        Возможность не описывать некоторые типы в статически типизированных языках тоже бывает: например, в C# есть dynamic, в Typescript — any. Но в хорошем коде это допустимо только в качестве локального исключения из правил, когда нужно вкрутить какой-нибудь незначительный костыль.


        1. Free_ze
          28.11.2018 13:27
          -1

          в C# есть dynamic, в Typescript — any.

          В dynamic/C# не означает потерю информации о типе в рантайме, в отличие от any/TS.


          1. mayorovp
            28.11.2018 13:43

            Разве в TS информация о типе в рантайме как-то теряется?


            1. Free_ze
              28.11.2018 14:25
              -1

              Разумеется) TS со своими чудными типами живет только на этапе компиляции, потом он деградирует до JS.
              А C# проверяет типы всю дорогу, dynamic лишь отключает проверку типов на этапе компиляции, а в рантайме они продолжают бросаться экепшнами, если что-то пошло не так.


            1. VolCh
              28.11.2018 15:51

              Иначе не было бы type guard'ов и то как костылей.


              1. mayorovp
                28.11.2018 15:53

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


                1. VolCh
                  28.11.2018 15:58

                  Как я их понимаю, на этапе проверки типов компиляции, анализатор делает предположение, что он возвращает реальный результат о типе как instanceof.


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


                  1. mayorovp
                    28.11.2018 16:02

                    Так ведь наличие методов потому и можно проверить, что информация о типе не потерялась, и ее можно вытащить через Object.keys, Object.getOwnPropertyNames или оператор in...


                    1. VolCh
                      28.11.2018 16:11

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


                      1. mayorovp
                        28.11.2018 16:19

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


                        1. VolCh
                          28.11.2018 16:58

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


                          1. mayorovp
                            28.11.2018 16:59

                            А где я писал про «средства проверки типа»? Информация о типе — не то же самое, что средства проверки типа.


                            1. VolCh
                              28.11.2018 20:31

                              А информация о структуре типа — не то же самое что информация о типе.


                      1. Druu
                        28.11.2018 16:37

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


  1. Anton23
    27.11.2018 19:34
    +1

    Фраза в конце порадовала)).


  1. justboris
    27.11.2018 19:35

    Про нейронную сеть интересно. А она точно нейронная? На каких данных обучалась?


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


    • Context in #reactjs is a crutch – никто так не пишет, в результатах гугла только этот самый твит. Больше похоже на русского программиста, который буквально перевел слово "костыль" на английский.


    • Есть твиты с картинками. Откуда нейронная сеть их возьмет? Поиск по картинкам в гугле ничего не дал, а в Яндексе находит только этот же самый твит. Получается, что ИИ сочинил текст, сделал его скриншот и вставил в твит? Слишком фантастично.



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


  1. Alexey_Zal
    27.11.2018 19:46
    -4

    Хабрачани, а почему все забывают, что динамическая типизация, практически, была всегда? В С++ всегда использовалась динамическое выделение памяти и в ней можно было творить что угодно. Главное не выйти за рамки и не забыть закрыть. Но её же не зря придумали? Просто нужно понимать когда притягивание за уши статический типизации переходит все рамки разумности — тогда используй принцип «Сделай сам». Хотя при таком подходе мой приятель обвиняет меня в раздвоении личности. Но это мой путь. Я всегда стараюсь использовать статическую типизация, но когда нужно передать, большой блок данных в другую подсистемы, то я могу и породить трёхэтажную не типизированную структуру. В разработке это, как правило, получается быстрее, да и адаптировать её при дальнейшем развитии проекта в неизвестном направлении — проще. Так что считаю что используя только статическую типизация вы себя искусственно ограничиваете. Хотя начинать свой жизненный путь, конечно, нужно со статический системы. Иначе — каша в голове обеспечена.


    1. NeoCode
      27.11.2018 20:00
      +3

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


      1. Alexey_Zal
        27.11.2018 21:02
        +1

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


        1. NeoCode
          27.11.2018 22:01

          Ну я тоже не сторонник абсолютно жестких систем типизации — когда между int и bool заставляют явно преобразовывать. Формально они правы конечно… но соглашение ноль==false, не ноль==true кажется вполне однозначным.
          Вариант С/С++ (впрочем с некоторыми улучшениями — что-то я бы усилил, что-то ослабил) мне нравится больше всего (хотя наверное здесь есть фактор привычки).


          1. areht
            27.11.2018 23:08
            +2

            > но соглашение ноль==false, не ноль==true кажется вполне однозначным.

            А зачем ноль преобразовывать в false? Откуда взялся этот ноль со значением false?


            1. unsafePtr
              28.11.2018 01:18
              +2

              В доисторические времени типа bool не существовало. Его симулировали на макросах брав за основу тип int. Любое число отличное от нуля было true, а если ноль то false.


              1. areht
                28.11.2018 05:47

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


                1. VolCh
                  28.11.2018 12:21

                  Удобно, многие проверки записываются короче типа `if (count)`, а не `if (count != 0)`. Ещё удобно преобразование null к false, особенно для nullable объектных типов


                  1. mayorovp
                    28.11.2018 13:44

                    Ага, особенно "удобна" вот такая форма условного оператора: if (count=0) :-)


                  1. areht
                    28.11.2018 14:11

                    Это не «удобно», это типа сэкономили 3 символа (на самом деле нет).

                    А «удобно» — это когда при виде `if (count)` не возникает вопросов:
                    — Это проверка на «проинициализирован ли count»?
                    — А count вообще проинициализирован?
                    — А count где-то инкрементируется? Точно один раз?

                    Вот `if (items.Any())` — удобно. Ни переменной лишней не надо, ни следить за ней.

                    Это даже не вспоминая про удобство `if (count = 0)`…


                    1. VolCh
                      28.11.2018 15:54

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


                      1. areht
                        28.11.2018 19:52
                        +1

                        > Это не просто экономия, это паттерн, распознаваемый с одного взгляда.

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

                        > паттерн сравнение со значением

                        Только семантика у него не про сравнение, так ещё и потерю выразительности в удобство запишите.


                      1. KostaArnorsky
                        29.11.2018 07:08

                        Вам распознаваемый, кому-то нет. Я оператор "!" снова начал использовать только из-за нытья других разработчиков, но где я могу задавать правила, все по прежнему пишут boolVal == false, потому что "!" маленький и незаметный.


                        1. PsyHaSTe
                          29.11.2018 12:46

                          Сравнение в bool как по мне вообще атавизм из си, вызванный как раз этой проблемой, как и йода-стайл if (5 == x)


                  1. PsyHaSTe
                    28.11.2018 15:41

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


          1. Cerberuser
            28.11.2018 07:38

            Явно преобразовывать между int и bool, говорите?

            В мою бытность студентом (шесть лет назад) у нас был компьютерный класс. Ubuntu на терминалах, первый урок — как перейти в консоль и выйти из случайно запущенного vi, код пишется в mcedit… Первые эксперименты (примитивная fork-бомба, завесившая намертво терминал), первые куски кода, про которые никто не понимает, как это работает, и прочие радости юного кодера.
            Одной из этих радостей был gcc, настроенный лично завкафедрой. -Werror — наш лучший друг, в этом я убедился ещё тогда; особенно, когда количество активных предупреждений начинает потихоньку переваливать за рамки очевидного.

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

            arr[indexes[i]]
            Ta-damm! Ошибка — за давностью лет уже и не вспомню, какая именно, но я, глядя на неё, решил попробовать явное приведение:
            arr[(int)indexes[i]]
            Фиг вам! Та же самая ошибка.

            … В общем, конечная строка кода выглядела так:
            arr[(int)(float)indexes[i]]
            И удаление любого из двух явных приведений типа приводило к выбросу предупреждения (ну а в силу -Werror — к ошибке компиляции).

            Явно преобразовывать между int и bool, говорите?..

            P.S. А ещё я про этот случай периодически вспоминаю при работе с Rust. Со всеми этими
            let int: i32;
            let float: f32;
            ...
            let double: f64 = (float as f64) * (int as f64);
            
            Но здесь хотя бы понятна суть: весь язык устроен так, чтобы не давать выстрелить себе в ногу, обрезав результат под выходной тип.


          1. yomayo
            28.11.2018 10:25

            соглашение ноль==false, не ноль==true кажется вполне однозначным.

            Это кажется.

            Возьмём логический тип. Пусть false – это 0. Тогда ~0 (не ноль) — это 0xffffffff или -1 в дополнительном коде. Применяя как операции «&», «|», «^»,, так и операции «&&», «||» мы получаем правильные результаты.

            Для числового типа это не так. Возьмём значение 1 и значение 2. И то, и другое — это true. Применив к ним операцию «&» (побитовое «и»), получаем 0, т. е. false. Получается, что true & true равно false. Для операции «^» тоже можно подобрать примеры противоречий.

            Вывод: между типами int и bool разница есть.


            1. sentyaev
              28.11.2018 15:19

              Вы почему-то смешали булеву логику и битовые операции.


              1. PsyHaSTe
                28.11.2018 15:43

                По старому соглашению, a && b это сокращенная запись a & b, где b не вычисляется, если a false. Аналогично с |.


                1. KostaArnorsky
                  29.11.2018 07:12

                  В С-подобных языках && и & два совершенно разных оператора.


                  1. PsyHaSTe
                    29.11.2018 12:47

                    Для bool-like типов данных они должны давать один и тот же результат.


                    1. KostaArnorsky
                      29.11.2018 20:24

                      А если в выражении вызов функции? А если функция с сайд-эффектом?


                      1. PsyHaSTe
                        29.11.2018 22:45

                        Какая разница? Вопрос про тип данных.

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


                        1. KostaArnorsky
                          30.11.2018 05:34

                          Разница в том, что && это не сокращенная запись &, это два совершенно разных оператора. В .NET даже определены для разных типов, можно 3 & 5, но не &&.


                          1. PsyHaSTe
                            30.11.2018 11:53
                            +1

                            Я не зря про bool-like говорил.


                            Для остальных типов побитовое И может быть любым.


                            если у вас есть


                            bool a = ...
                            bool b = ...
                            bool c = a && b;
                            bool d = a & b;

                            результаты c и d должны совпадать для любых a,b.


                            1. KostaArnorsky
                              30.11.2018 17:49

                              В C/C++ int bool-like (хотя что это вообще такое?)

                              #include <stdio.h>
                              
                              int main()
                              {
                                  int a = 1;
                                  if (a)
                                  {
                                      printf("I'm a and I like true!\n");
                                  }
                                  
                                  int b = 2;
                                  if (b)
                                  {
                                      printf("I'm b and I like true as well!\n");
                                  }
                                  
                                  bool c = a && b;
                                  int d = a & b;
                                  printf("bool c = a && b: %04X\n", c);
                                  printf("int d = a & b: %04X\n", d);
                                  if (!d)
                                  {
                                      printf("I will not argue with the standard");
                                  }
                              }
                              

                              Я надеюсь, вы все так пишите, удачи с отладкой.


                              1. PsyHaSTe
                                30.11.2018 19:36

                                Так никто и не спорил, что В С эта часть стандарта откровенно плохая.


                                1. KostaArnorsky
                                  30.11.2018 20:05
                                  -1

                                  Все в порядке с этой частью стандарта, хотите стрелять себе в ноги — стреляйте.


          1. yomayo
            28.11.2018 11:57

            Есть момент, который подрывает веру в статическую типизацию. Допустим, пользователь вводит строковые данные. Эти данные надо обработать функцией «распознать(строка)». Эта функция должна вернуть значение числового типа (если пользователь ввёл число), дату (если введённые данные похожи на дату) или исходную строку (если ничего распознать не удалось). Статически определить такую функцию не получится. Функция должна будет возвратить не только значение, но и типовую переменную, поскольку тип заранее не известен.


            1. mayorovp
              28.11.2018 12:01
              +1

              Все возможно. Функция без проблем может вернуть number | string (Typescript), Either Int String (Haskell) или std::variant<int, std::string> (C++).


              1. yomayo
                28.11.2018 13:43
                -1

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

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

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


                1. mayorovp
                  28.11.2018 13:45

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


                1. TargetSan
                  28.11.2018 14:11
                  +2

                  Да написать-то так можно. но получается потом не по фэншую. Дальше придётся писать проверки типов

                  В динамике вам точно так же придётся писать проверки для обработки собственно значений


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

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


            1. Meloman19
              28.11.2018 12:06

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


            1. nexus478
              28.11.2018 13:50

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

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

              Если же вам все-таки надо вызвать определенную логику обработки в зависимости от типа, то это например в C# это можно быть сделано вот так
              public void Recognize(string value)
              {
                  DateTime date;
                  int number;
                
                  if (DateTime.TryParse(value, out date))
                  {
                      HandleDate(date);
                  }
                  else if (int.TryParse(value, out number))
                  {
                      HandleInt(number);
                  }
                  else
                  {
                      HandleString(value);
                  }
              }

              Для абстрагирования от типов можно также можно использовать ad-hoc полиморфизм (перегрузка операторов или методов), параметрический полиформизм (дженерики) или полиморфизм подтипов (абстрактные классы, интерфейсы). Инструментов куча.
              И вообще, что это у вас за поле такое в интерфейсе, которое может быть и строкой, и числом, и датой?


              1. KostaArnorsky
                29.11.2018 07:41

                Да ну, чушь. Вот 2018.12 — это число? Вы уверены?

                Console.WriteLine(decimal.Parse("2018.12", CultureInfo.InvariantCulture).ToString());
                Console.WriteLine(DateTime.Parse("2018.12", CultureInfo.InvariantCulture).ToString());

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


            1. creker
              28.11.2018 13:56

              В C# если совсем прям не париться, то можно вернуть object и в рантайме проверять тип на выходе. Только вот, зачем? Я не помню в практике задач просто распознать. Я всегда знаю заранее, что конкретно я хочу распознать. Если я распознаю то не знаю что, то зачем мне вообще это делать, пусть строкой остается. Когда парсится какой-нить JSON, то я все равно проверяю все типы на выходе, чтобы быть уверенным, что мне пришли правильные данные, а не строка, в которой может быть какой угодно тип.


            1. zagayevskiy
              29.11.2018 00:37

              Прекрасно получится, например в Kotlin. Примерно так

              sealed class RecognitionResult 
              data class RecognizedNumber(val value: Int): RecognitionResult()
              data class RecognizedDate(val date: Date): RecognitionResult()
              data class RecognizedString(val str: String):RecognitionResult()
              
              fun recognize(input: String): RecognitionResult = TODO("implement")
              
              fun main(args: Array<String>) {
                  when(val result = recognize(args.firstOrNull() ?: return) {
                      is RecognizedNumber -> print(result.value)
                      is RecognizedDate -> print(result.date)
                      is RecognizedString -> print(result.str)
                  }
              }
              


              Всё статически определено, проверено и обработано. Профит.


            1. KostaArnorsky
              29.11.2018 07:16

              Пользуйтесь

              abstract class UserInput<T>
              {
                  public string Input { get; set; }
              
                  public abstract T Value { get; }
              
                  public abstract bool IsValid { get; }
              }


              1. areht
                29.11.2018 12:06

                И как этим пользоваться? Вызов функции «распознать(строка)» можете показать?


                1. KostaArnorsky
                  29.11.2018 19:41

                  ?Если вам нужен синтаксический анализатор — используйте синтаксический анализатор, опишите правила и распознавайте ваши строки. Вашу функцию можно использовать как одноразовое решение, но надо понимать, что это костыль, потому что habr.com/post/431250/#comment_19430178
                  В остальных 99% случаев вы знаете требуемый тип. И не забываем про
                  про whitelisting by default.


                  1. areht
                    29.11.2018 20:21
                    +1

                    А предыдущие 2 комментатора смогли осилить ТЗ, а не рассказывать, что вместо пары if надо синтаксический анализатор настраивать.

                    > Вот 2018.12 — это число? Вы уверены?

                    Я уверен, что неуверенность в этом только вам по ночам спать мешает.
                    Да, число. Если бы дату хотели ввести — ввели бы увереннее, например «2009-06-15T13:45:30.0000000-07:00»


                    1. KostaArnorsky
                      29.11.2018 22:26

                      А где вам такое понадобилось? Мне за всю практику понадобилось ровно один раз, где пользователи могли писать что-то типа sum(workdays({requests.submited}, 11/03/2015)) * 8. Первым делом мы выкинули багованные велосипеды неосльщиков, взяли PEG и описали нормальную грамматику. А вы для чего тип ввода определяете?


                      1. areht
                        29.11.2018 22:48

                        Ну, задание не я придумывал. Но, например, указывать «вчера» как "-1" наравне с «28.11.2018» с подобным парсингом в (datetime|int) где-то под капотом вполне могу представить.


                        1. KostaArnorsky
                          30.11.2018 05:21

                          Я бы наверно regex написал в этом случае. Тем более что JS Date 28.11.2018 все равно не понимает, а -0.01e+2 вполне себе валидное число, только оно вам такое надо?


                          1. areht
                            30.11.2018 12:50

                            Вы опять какую-то свою проблему решаете.

                            > -0.01e+2 вполне себе валидное число

                            Понимаете в чем дело, ввод "-1" делают что бы быстро(!), не включая мозг, ввести дату «вчера». Если вам быстрее ввести -0.01e+2, то и оно корректно обработается — флаг в руки, чем бы дитя не тешилось.

                            То у вас синтаксический анализатор, что бы можно было ввести как минимум sum(workdays({requests.submited}, 11/03/2015)) * 8, то нельзя -0.01e+2… Не понимаю.


                            1. KostaArnorsky
                              30.11.2018 17:26

                              Извините, самое главное забыл, функция в любом случае должна возвращать дату или невалидное значение (e.g. undefined or null), так что ваш пример мимо. Так можно какой нибудь реальный пример, где требуется возвращать разные типы?


          1. vintage
            29.11.2018 09:14

            А NaN — это true или false? A -0? A Infinity?


            1. PsyHaSTe
              29.11.2018 12:49

              Да просто заучиваете всю табличку и всё. Заодно тренировка памяти!


    1. evocatus
      27.11.2018 20:18
      +1

      Динамически типизированному LISP скоро 60 лет.


      1. impwx
        27.11.2018 22:28
        -1

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


        1. evocatus
          27.11.2018 23:36
          +1

          понимая под LISP семейство языков (Common LISP, Scheme, Clojure, ...):

          Emacs, Maxima, некоторые части AutoCAD (и там есть встроенный интерпретатор LISP), полётная система Boeing 747, Apache Storm, Datomic (база данных), бэкэнд Walmart, оригинальный Reddit, HackerNews,…

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


          1. impwx
            28.11.2018 11:29

            Полноты по Тьюрингу и очень упорного программиста достаточно, чтобы написать что угодно на чем угодно. Было бы странно, если бы на LISP не было вообще ни одного серьезного приложения — сама по себе идея очень заманчивая. Однако всего пара десятков проектов за шестьдесят лет — это все-таки показатель.


            1. evocatus
              28.11.2018 13:36

              О честном сравнении не может быть речи в условиях, когда:

              • 99,9% всех образовательных программ в школе и институте уже 40 лет (если не больше) обучают с самого начала только структурному программированию (в последнее время ООП)
              • всякие пиарщики ездят по ушам менеджерам, а преподают люди давно отошедшие от разработки (или никогда ей не занимавшиеся), в итоге образованием и управлением занимаются люди, до сих пор пересказывающие сказки из 90-х про ООП
              • на самых популярных платформах по подготовке школьников по программированию и проведению олимпиад (informatics.msk.ru, Codeforces, contest.yandex.ru) есть даже PHP и OCaml, но нет ни одного диалекта LISP


              1. impwx
                28.11.2018 13:43

                Осталось только понять, что из этого причина, а что — следствие…


              1. Druu
                28.11.2018 14:00

                99,9% всех образовательных программ в школе и институте уже 40 лет

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


                1. evocatus
                  28.11.2018 16:44

                  Как проходят? Как у некоторых: по языку в семестр? Пользы от такого зоопарка никакой, только каша в голове.


                  1. Druu
                    28.11.2018 16:48

                    Как проходят? Как у некоторых: по языку в семестр? Пользы от такого зоопарка никакой, только каша в голове.

                    А надо 5 лет лисп учить?


                    1. evocatus
                      28.11.2018 19:00

                      А что вы понимаете под учить? Синтаксис можно объяснить за 5 минут, а вот начать думать по-другому, научиться решать задачи по-другому — это требует время.


                  1. KostaArnorsky
                    29.11.2018 07:24

                    Я все языки проходил по одному в семестр, C только два, да и то второй был системное программирование с обязательным условием писать на C. И был Lisp и даже Prolog.


                1. Free_ze
                  28.11.2018 16:48

                  Вести из параллельной вселенной (=


        1. igrishaev
          28.11.2018 10:59

          Полистайте https://lisp-lang.org/success/
          Проекты на Лиспах писали еще в то время, когда никаких Гитхабов не было.


        1. gatoazul
          28.11.2018 18:53

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

          Помните, как Маяковский писал о Хлебникове? Есть поэты для читателей, а есть поэты для поэтов.


          1. impwx
            28.11.2018 19:09

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


    1. impwx
      27.11.2018 22:02
      +1

      Это не динамическая типизация. Динамическая типизация в вашем примере — если бы у вас все аргументы и возвращаемые значения всех функций были void *.


      1. alexesDev
        28.11.2018 00:29

        Не void*, а {type,void*}, как std::variant


        1. 0xd34df00d
          28.11.2018 01:56

          Тогда уж any, а не variant.


          1. alexesDev
            28.11.2018 02:00

            Вы писали на C++ под nodejs или ещё что? V8::Value похож на std::variant, точно не на any. В any можно положить все, что угодно, а std::variant это предопределяет. v8docs.nodesource.com/node-0.8/dc/d0a/classv8_1_1_value.html


            1. 0xd34df00d
              28.11.2018 02:01

              Нет, я вообще не писал под nodejs. Правда, я не понимаю, причём тут вообще V8, если речь про аналог динамического типа (а это именно тег + значение) в C++.


              1. alexesDev
                28.11.2018 02:11

                Покажите как адекватно можно сложить два std::any? Возможно я не понимаю как это делается. Для std::variant все просто — static_visitor и простым перечеслением, что делать в странных ситуациях, когда нужно int и строку сложить. Я использовал этот подход, когда писал игрушечные языки. Он удобный, простой и даже, неожиданно, быстрый, если не нужно делать тяжелых конвертаций.


                1. 0xd34df00d
                  28.11.2018 02:33
                  +1

                  Покажите как адекватно можно сложить два std::any?

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


  1. gBear
    27.11.2018 19:47
    +3

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

    Все то, что так вас раздражает — это все не от «динамики» типов, а от их «weakly typed» :-) Которая, вполне себе, может быть и статической при этом. Обратное, кстати, тоже верно… в том смысле, что динамическая типизация ортогональна сильной. И языки с сильной динамической типизацией вполне себе есть.

    Так что, «динамика» тут вообще не причем… даже в плане «статических проверок». Динамическая типизация, совсем не означает их отсутствия. А вот слабая типизация (хоть даже и статическая) — в лучшем случае — сводит их к, скажем так, слабо полезной формальности.

    P.S. Весьма сильно улыбнуло противопоставление «динамики» и ООП. На всякий — вообще ортогональные вещи.
    P.P.S. И да… *система типов* — это, судя по всему, не совсем то, что вы думаете.


    1. PsyHaSTe
      28.11.2018 15:46

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


  1. NeoCode
    27.11.2018 20:05
    +4

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


    1. evocatus
      27.11.2018 20:30

      Не JS, но от автора динамического языка, который был очень хорошо продуман. youtu.be/2V1FtfBDsLU?t=1610 (временная метка на то место, где он говорит о разных ошибках, в т.ч. связанных с типами и почему это не так важно)

      А в этом интервью Рич на вопрос о динамической типизации ответил, что «уважает право разработчика делать что угодно»


    1. justboris
      27.11.2018 20:31
      +2

      Я большую часть времени пишу на Javascript, а иногда приходится нырять в Java и Typescript. Для себя нахожу вот такие проблемы.


      Есть какая-то функция, например создание модального диалога


      function createDialog({title, content, footer, onSubmit, onDismiss, ...andMore}) {
        // do something
      }

      В типизированном подходе вам понадобится описать интерфейс DialogOptions и перечислить эти поля еще раз. А если внутри появится еще функция createDialogBody(), которая будет принимать только часть параметров, то ей понадобится еще один интерфейс. Эта писанина утомляет.


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


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


      1. Alexey_Zal
        27.11.2018 21:09

        Согласен на все 100%.


      1. NeoCode
        27.11.2018 21:29
        +5

        Непонятно что вы имеете в виду с интерфейсами. Я бы просто написал (псеводкод на некотором типизированном языке, «void=>void» — тип для функции, не принимающей никаких параметров и не возвращающей никакого значения)

        function createDialog(string title, string content, string footer, void=>void onSubmit, void=>void onDismiss) {
          // do something
        }

        Меня вот просто выводила из себя (до желания стучаться головой о стенку) ситуация, когда я смотрю код на php или js, там какая-то функция с аргументами, или просто какие-то переменные, и я НЕ ПОНИМАЮ, каких они типов. Чего мне ждать — числа, строки, структуры (если структуры то какой, с какими полями каких типов), функции (опять же с каким списком аргументов)? Что я имею права с ними делать а что нет? Ну хорошо, что в вебе в основном строки. В основном — но не всегда. Вот честно, не представляю как вы с этим живете :)


        1. Alexey_Zal
          27.11.2018 21:36

          Но сколько на отписывание структур нужно времени? А если эта функция используется одним разработчиком в тестовом проекте и в одном месте?


          1. NeoCode
            27.11.2018 21:54

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


          1. khim
            28.11.2018 00:22
            -1

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


        1. aikixd
          27.11.2018 21:44

          Как-то, помню, уже с 8 летним опытом писал на JS на Ember. Но тут по интернет и работа закончилась. Вот так и живём.


        1. justboris
          27.11.2018 23:34
          +1

          У такого размазывания по аргументам есть и обратная сторона. Функции с большим числом аргументом считаются плохим кодом и предлагается избавляться от них созданием DTO. Вот эти DTO как раз утомительно писать, особенно в Java, где на каждое поле еще и геттер/сеттер нужно создать. Да, в редакторах есть макросы, но все равно получается простыня кода просто для того чтобы передать данные из одного модуля в другой.


          Вот честно, не представляю как вы с этим живете

          Читаем документацию, вставляем параметры как там описано. Еще современные редакторы (IDEA и VSCode) понимают декларации Typescript и показывают их для JS кода. Таким образом можно облегчить себе работу с библиотеками, не обкладывая при этом типами весь код целиком.


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


          1. areht
            28.11.2018 00:14
            +2

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

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

            > А сколько времени займет построение графика по данным из JSON на том же C#?

            Вы, наверное, подразумеваете, что надо типы создавать под JSON, и это долго? В том же C#, внезапно, есть dynamic.

            dynamic stuff = JsonConvert.DeserializeObject("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
            string name = stuff.Name;
            string address = stuff.Address.City;


            1. justboris
              28.11.2018 00:30
              +1

              Это действительно внезапно! Потому что теперь я не понимаю автора статьи еще больше. Если в C# есть dynamic, что чего плохого в том, что в Typescript иногда используется any?


              1. areht
                28.11.2018 02:42

                Ещё в C# есть goto, но в реальной жизни не встречается.

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


                1. KvanTTT
                  28.11.2018 10:58
                  +1

                  Можно ли обобщить до такого?

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


                  1. areht
                    28.11.2018 12:06

                    Ну как, динамический язык может и прилетевший {'Age': '42' }, вместо {'Age': 42 } простить, и stuff.Address.City как stuff.?Address.?City обработать.

                    Так что я исключительно про dynamic+C#+мозг людей привыкших к статике.


                  1. PsyHaSTe
                    28.11.2018 15:58

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


                    Но в 21 веке можно получит и то и то бесплатно. Например


                    type Simple = JsonProvider<""" { "name":"John", "age":94 } """>
                    let simple = Simple.Parse(""" { "name":"Tomas", "age":4 } """)
                    simple.Age
                    simple.Name2 // compile-time error: no field or property 'Name' found


                1. IvanNochnoy
                  28.11.2018 14:45

                  Ещё в C# есть goto, но в реальной жизни не встречается.

                  Встречается, если нужно выйти из вложенных циклов.


                  1. Free_ze
                    28.11.2018 14:51

                    В редкой задаче есть больше двух циклов и нет плохокода одновременно.


                  1. PsyHaSTe
                    28.11.2018 15:50

                    Тогда пишется функция, где пишется return. Goto ни в одном проекте, слава богу, не встречал.


                    1. Szer
                      28.11.2018 17:55

                      Тогда пишется функция, где пишется return. Goto ни в одном проекте, слава богу, не встречал.

                      ОЧЕНЬ быстрые корутины на дотнете написаны с помощью goto


                      https://github.com/Hopac/Hopac/blob/6b4fe8e89e95aa9f2119e96f19d37cfe541e7715/Libs/Hopac.Core/Engine/Worker.cs#L129


                      1. Free_ze
                        28.11.2018 18:03
                        +1

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


                      1. PsyHaSTe
                        28.11.2018 18:41
                        +1

                        А еще yield return/ async-await разворачиваются в стейт-машины на goto, но это не повод так писать в проде.


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


                  1. KostaArnorsky
                    29.11.2018 07:44

                    Я больше одного return пишу с осторожностью. Приятно с таким кодом работать?


                    1. mayorovp
                      29.11.2018 08:35

                      Лучше уж два return в функции, чем goto или вспомогательный флаг…


                    1. PsyHaSTe
                      29.11.2018 12:50

                      Очень приятно. https://habr.com/post/348074/


                      1. KostaArnorsky
                        29.11.2018 19:55

                        Я же не говорил не использовать, я говорил — с осторожностью. Некоторый примеры я бы отрефакторил, к примеру, updatePostAction можно разбить на две. $failResponse переменная вообще не нужна, в success pass она даже не используется, вынести это в третий метод.


                    1. Kroid
                      29.11.2018 14:56

                      Да, приятно. Особенно с теми языками, где нет switch-case.


                      1. KostaArnorsky
                        29.11.2018 19:58

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


                        1. Kroid
                          30.11.2018 14:47
                          +2

                          Ранние return в общем случае — это избавление от вложенных блоков. Проверили что-то — если false, то до свидания. Как это может быть неприятно?


                          1. areht
                            30.11.2018 17:49

                            return бывает 2 типов:
                            1) проверка инвариантов — тут да, проверили, false, досвидания
                            2) из логики функции. И вот тут такое себе. А успели ли мы подготовить данные? А корректно ли всё закрыли? И записали ли про выход в лог? И тут ради раннего возврата начинают какой-нибудь try finally накручивать на ровном месте (к вопросу об избавлении от вложенных блоков)…

                            Вот нет ощущения, что это всегда прям хорошо.


                            1. TheShock
                              30.11.2018 17:51

                              2) Все эти вопросы отпадают при нормальной декомпозиции функции.


              1. unclejocker
                28.11.2018 08:51
                +2

                Ну в шарпе и unsafe есть, и Span со stackalloc, но то не значит что ими надо всю программу обмазывать. Каждому инструменту свое место.


              1. PsyHaSTe
                28.11.2018 15:50

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


          1. mayorovp
            28.11.2018 08:30
            +1

            Читаем документацию, вставляем параметры как там описано.

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


      1. impwx
        27.11.2018 22:17
        +3

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

        Впрочем, по поводу хакатонов соглашусь. Если скорость важнее качества — то ДТ возможно будет лучше.


        1. aikixd
          27.11.2018 22:49

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


        1. justboris
          27.11.2018 23:48

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

          До таких дурацких моментов еще нужно дорасти, и не факт что это случится. На JS можно быстро накидать интерфейс (какой-нибудь отчет с графиками, например) и показать его людям. Примерно для тех же целей аналитики используют Jupyter Notebook с динамически типизированным Python.


          А сколько времени займет построение графика по данным из JSON на том же C#? Без сарказма, правда интересно, как бы вы это сделали и какие библиотеки использовали.


          1. impwx
            28.11.2018 11:11

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

            Касательно инструментов для построения графиков и анализа данных — конечно, у Python инфраструктура получше, однако и дотнет кое-что может. Например, в Azure Notebooks поддерживаются как Python/R, так и статически типизированный F#.

            Лично я для исследовательских целей использую LINQPad — это REPL-среда для C#/VB/F#. Там можно использовать все, что есть в самом языке\фреймворке (LINQ, любые Nuget-библиотеки), и плюс встроенные утилиты (отображение таблиц, графиков, подключение к БД). Отрисовка графика по JSON выглядит примерно так:

            Картинка


            1. justboris
              28.11.2018 11:44

              Спасибо за объяснения! Может быть, пригодится мне в будущем.

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


            1. 0xd34df00d
              28.11.2018 18:08

              Оно пока есть только в F#

              Не только.


              1. impwx
                28.11.2018 18:23

                Вы правы. Я имел в виду среди тех языков, которые перечислил выше.


          1. IvanNochnoy
            28.11.2018 15:39

            Хм, Newtonsoft.Json и OxyPlot. Не так уж и сложно.


      1. eugenk
        28.11.2018 01:41

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


        1. justboris
          28.11.2018 02:40

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

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


          1. AlexHell
            28.11.2018 02:55

            А еще Find Usages. Вот есть у меня в C# тип MySuperType, и надо мне найти где он юзается: где конструируется — в одном из case, в другом — вообще все использования, объявнения его как параметра в метод и как тип возврата.
            Что я буду делать в JS? Т.е всеравно TypeScript вам тут нужен будет если проект не прототип, и даже не факт что все эти IDE современные по TS помогут вам сделать то же самое что с системой типов элеиментарно уже десятки лет IDE делает.


            1. justboris
              28.11.2018 03:04

              В современном Javascript активно рекомендуется к использованию синтаксис import something from './something'. Он отлично статически анализируется и найти все места использования something не составляет труда. Работает в том числе и автоматическое удаление/добавление импортов.


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


              Здесь мем


              1. AlexHell
                28.11.2018 16:58

                вам аналогичный ответ — что я ответил ниже — про добавление свойства в объект. И да = объекта у вас не может не быть, он есть. Вы же в своей функции чтото делаете с каким-то переданным JS объектом, пусть он и без класса.
                Был X,Y, надо Z добавить. Удачи добавлять. Искать не надо вам. Ну да без поиска попробуйте.


                1. justboris
                  28.11.2018 18:12

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


            1. KostaArnorsky
              28.11.2018 08:02

              В JS вам это просто не понадобится, как вам уже ответили: нет типов — нет проблем. В JS вы будете искать, где это функция вызывается — обычный тестовый поиск по проекту. Поэтому давайте уникальные имена вашим функциям в JS! Умные разработчики еще псевдо неймспесы используют и ограничивают область видимости по возможности. Или просто используют TypeScript.


              1. Free_ze
                28.11.2018 12:07
                +1

                давайте уникальные имена вашим функциям в JS!

                Пока все прогрессивное человечество стремится к простоте и наглядности наименований…


              1. AlexHell
                28.11.2018 16:56

                Текстовый поиск без самантики в 21 веке? Статические типы и семантика имени что это объект или класс или переменная — были придуманы как раз для облегчения работы.
                И допустим я не знаю имя функции. Я знаю что у меня есть сущность у которой X,Y координаты, и мне надо Z добавить. Какую функцию мне искать? Все которые работают с объектом у которого есть X,Y, но пока нет Z. Без семантики — кучу головной боли.
                И да такие задачи сплошь и рядом в реальной разработке. Пример с X,Y,Z для упрощения. Реально бывает кучу других задач по расширению кол-ва свойств у сущности. А если вы сущности не ввели явно, а держите их в голове — ну удачи пройтись поиском.


                1. KostaArnorsky
                  28.11.2018 21:56

                  Я где-то сказал, что это хорошо и правильно? Вопрос был, как искать — я ответил.
                  И пример на самом деле не релевантный — вы же добавили Z с какой-то целью, вот там, где вам Z понадобилось — там и меняйте. Сразу видно, что на JS не пишите, новые свойства вообще не проблема (если не использовать ...), намного большая проблема, если вы удалили/изменили существующее свойство Z. Как узнать, что оно нигде не используется? Даже если вы нашли все места, что если в соседнем бранче новую функцию написали, в которой по прежнему Z используется? Это никак не обнаружится при мереже, компиляции нет… ну вы понимаете, только тесты. То, что вам может прийти любой мусор — вот это проблема.


              1. TheShock
                29.11.2018 05:47
                +1

                давайте уникальные имена вашим функциям в JS

                Ага. Ну, к примеру, pipe, it, connect, put, dispatch и другие очень емкие и уникальные словечки из современных хипстерских библиотек. Сколько у вас будет совпадений со словом «put»? Или «it». А еще у нас есть куча нод-модулей, где все функции повторяются по несколько раз, у этих функций разные версии и ты точно не можешь сказать, какая именно из этих используется. Знаете, иногда типизация TypeScript ломается. И тогда я беру тазик и могу наблевать в него до самых краев пока разберусь во всем при помощи вашего совета.


                1. KostaArnorsky
                  29.11.2018 07:57
                  -1

                  А еще у меня во всех проектах был jQuery c noConflict, никогда $. А еще можно явно указать какую функцию из какого модуля использовать. Еще можно попробовать все зависимости прописать, но пока не получалось. Но кто сказал, что будет легко? Зато работало, а не полная консоль ошибок.


                  1. TheShock
                    29.11.2018 08:43
                    +1

                    То есть вы пользуетесь менее удобными инструментами, чтобы было тяжелее? Я правильно вам понял?


                    1. KostaArnorsky
                      29.11.2018 11:58

                      Вы можете предложить альтернативу?


                      1. TheShock
                        29.11.2018 20:53
                        +3

                        Конечно. Статическая типизация и никакого «текстового поиска». Я 7 лет писал на JS, а потом попробовал писать на C#. И осознал, насколько неудобно мне было все семь лет и какие жуткие и неудобные костыли я использовал. Теперь возвращаюсь на JS или с использованием TS (хотя это и не настолько круто как C#) или на скрипты до 50 строк — весь код должен умещаться на экран.


                        1. KostaArnorsky
                          29.11.2018 22:41

                          А как вы исполняете C# в браузере? Я пару лет назад смотрел прикольную либу для трансплайна в JS, но это было далеко от production-ready. А в скриптах на 50 строк никаких библиотек никогда не используете? А из TS?


                          1. ApeCoder
                            29.11.2018 22:45

                            blazor есть, но тоже не production ready


                          1. TheShock
                            29.11.2018 23:40

                            А как вы исполняете C# в браузере?
                            В браузере, увы, никак. Под браузер я пишу под ТС.

                            А в скриптах на 50 строк никаких библиотек никогда не используете
                            Да. Если скрипту нужна библиотека — значит он уже достаточно большой, чтобы использовать TS.


          1. eugenk
            28.11.2018 03:30

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


            1. Cerberuser
              28.11.2018 07:46

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


            1. justboris
              28.11.2018 12:07

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

              В этом и суть. С типами у вас каждая задача – "серьезный случай". В JS типы можно принести позже, когда совсем прижмет. Или не приносить, если и без них нормально, как в d3 или jsplumb.


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


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


              1. eugenk
                28.11.2018 14:05

                В d3 исходники разбиты на маленькие модули (например). Эта модульность работает почти как типизация

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


              1. AlexHell
                28.11.2018 17:00

                Ваши аргументы про «когда приспичит внести типы… да как в d3» относятся к использованию готового API / интерфейса. А попробуйте сам свой API / интерфейс для лругих создать, тогда и скажете что не надо это все.

                О да я глянул только что вашу ссылку на arc
                github.com/d3/d3-shape/blob/master/src/arc.js
                Это жесть, куча копипаста в духе
                " return arguments.length? (endAngle = typeof _ === «function»? _: constant(+_), arc): endAngle;"
                И всеравно не понятны типы возврата. Тут будет приведение к int или float будет или строка? И в куче аналогичных случаев тот же вопрос.
                А когда возврат структуры — вам еще внутри функции покопаться

                И только не говорите что неважно. Вы с таким же успехом тогда аргументируете к вопросу «вот вам массив байт — в нем что — целое число и вещественное и с какой точностью, и какая в нем endianness если он сериализован, и что там за переменные в ней и каков размер массива если это он». А вы еще на функции будете проверять (по духу d3). А в колбеках будете выяснять что у вас в аргументах — строки (которые легко уже превратяться в int или float по вашей надобностью) или JSON сериализованная форма (и какого объекта серилизованного) — что даже в этом случае — строка всеравно, а семантика разная. Ну не дураки же придумали семантику. Ну потратите лишнее время на выяснения.

                **

                function arc() {
                var buffer,
                r,
                r0
                r1

                и
                export default function() {
                var innerRadius = arcInnerRadius,
                outerRadius
                Это и есть объект. И система типов вот она. Даже автор d3 вводит. Только костылями, без явного объявления class (как в подмножестве 2016 или 2015 года не помню уже)


                1. KostaArnorsky
                  29.11.2018 08:04

                  Вы еще не поняли всю глубину пропасти, в JS нет int.


            1. VolCh
              28.11.2018 12:33

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


              1. khim
                28.11.2018 12:39
                +1

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

                Только не берите C/C++ — неплохие языки, но количество ногострелов, прилагаемых «в комплекте», что называется «зашкаливает»…


                1. VolCh
                  28.11.2018 12:55
                  +1

                  Именно. Более-менее так и написан, а все использования «киллер-фич» динамики как таковой строго задокументированы и частично статически и(или) динамически типизированы (например с помощью *doc аннотаций), чтобы помочь и IDE, и разработчику понять, что происходит.

                  А мне непонятно зачем его брать. Думаю, за месяц-два я легко перейду с PHP на Java/C#, как когда-то перешёл на PHP и JS с C/C++, пускай и не все их возможности знать буду. Но зачем? Что это даст мне лично и что бизнесу, на который я работаю? Ну вот есть у нас сейчас TS — за почти полгода работы ни разу он мне не помог обнаружить какой-то баг.


                  1. faiwer
                    28.11.2018 13:04

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

                    Ух. Первый негативный отзыв о TS который мне попался на глаза. Реквестирую статью про это :)


                    1. VolCh
                      28.11.2018 16:02

                      О чём об этом?


                  1. mayorovp
                    28.11.2018 13:50

                    А вы не думали, что он, вместе с автодополнением в IDE, мог вам помочь этот баг не написать?


                    1. VolCh
                      28.11.2018 16:04

                      Не заметил существенной разницы между JS с ключом only type based competition и TS в одной и той же IDE


                      1. mayorovp
                        28.11.2018 16:17

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


                        Мне вот достаточно написать $.extend(Foo.prototype, { ... }) — и IDE уже не может понять что все методы внутри фигурных скобок могут обращаться к свойствам Foo в контексте this.


                        Или viewModel.BeginPeriod.typed — и IDE перестает понимать что происходит, потому что свойства typed у ko.observable нету, а тайпинг через jsdoc чужому классу не добавить.


                        1. VolCh
                          28.11.2018 18:37
                          -1

                          Стараюсь не писать сложного кода — KISS и всё такое :) И использовать библиотеки, о которых знает моя IDE (пускай она и использует для своего знания объявления на TS)


                          1. mayorovp
                            28.11.2018 20:05
                            +1

                            Но если ваша IDE использует тайпинги от TS даже для JS — вы уже не можете использован аргумент "Не заметил существенной разницы между JS с ключом only type based competition и TS в одной и той же IDE" в качестве доказательства ненужности TS.


                            1. VolCh
                              29.11.2018 14:54

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


                              Но вот при написании своего кода я особой разницы не замечаю.


                  1. creker
                    28.11.2018 14:04

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


                    1. VolCh
                      28.11.2018 16:06

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


                      1. ApeCoder
                        28.11.2018 16:12
                        +1

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


                        1. VolCh
                          28.11.2018 16:52

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


                          1. ApeCoder
                            28.11.2018 17:28

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


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


                      1. 0xd34df00d
                        28.11.2018 18:10
                        +1

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


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


                        1. VolCh
                          29.11.2018 15:29

                          Вы не путаете статическую типизацию со строгой? Гарантии даёт строгая прежде всего.


                          1. 0xd34df00d
                            29.11.2018 15:43

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


                            1. VolCh
                              29.11.2018 16:41

                              Лучше поздно чем никогда. А иногда лучше даже поздно чем рано :)


                      1. creker
                        28.11.2018 18:58

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


                        1. VolCh
                          29.11.2018 15:31

                          Они широко используют автоматический вывод типов, а также информацию о типах, имеющуюся в синтаксисе типа function name(string $param) или в *doc-аннотациях.


                          1. creker
                            29.11.2018 16:50

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

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


                            1. VolCh
                              29.11.2018 16:54

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


                              1. creker
                                29.11.2018 16:56
                                +1

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


                                1. VolCh
                                  30.11.2018 10:49
                                  -1

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


                                  У TS есть плюсы, но есть и минусы.


                                  1. Free_ze
                                    30.11.2018 11:14

                                    TS — это же надмножество JS, выучил его — второй уже знаешь.


                                    1. VolCh
                                      30.11.2018 11:20

                                      Что интересно, я не нашёл полноценных учебников TS в своё время, в которых JS вообще не упоминался бы (ну пускай "под капотом компилируется в JS, но вам это не нужно", как может быть в учебнике по C про машинные коды или в учебнике по Java про байт-код). В подавляющем большинстве литературы речь о различиях, и подразумевается что JS уже знаешь. Разве что классы описаны "с нуля", но это как я понимаю, потому что по факту они пришли в JS из TS.


                                      1. Free_ze
                                        30.11.2018 11:31

                                        Если честно, я полноценных учебников по TS не встречал в принципе (не умоляет годности официальной документации, но, как вы уже заметили — требует старта со знания JS).
                                        Что, кстати, огорчает, ибо в учебниках по JS я встречал достаточно мало информации насчет грамотного построения архитектуры ПО в контексте фронтенда, в стиле «Think in Java» и других хороших книжек.


                  1. zagayevskiy
                    29.11.2018 11:25
                    +1

                    Так эта… может он вам помог не допустить баги?


                    1. VolCh
                      29.11.2018 15:32

                      Помог незаметно как-то?


                      1. zagayevskiy
                        29.11.2018 16:08
                        +1

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


                        1. VolCh
                          29.11.2018 16:42

                          Вот что значит "не дали написать"?


                          1. PsyHaSTe
                            29.11.2018 16:46

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

                            А компилятор — тот же стат. анализ, только не отключаемый.


                            1. VolCh
                              29.11.2018 16:51

                              Я не пойму про какой сценарий речь идёт. Мой:


                              • пишу код на TS
                              • запускаю компилятор
                              • не вижу ошибок

                              В какой момент мне не дали создать баг?


                              1. PsyHaSTe
                                29.11.2018 16:57

                                Тогда вы не совершаете ошибок типов, что ведет нас к двум возможным вариантам:


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


                              1. zagayevskiy
                                29.11.2018 19:41

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


                                1. VolCh
                                  29.11.2018 21:34

                                  Мои ошибки на другом уровне :)


                                  1. PsyHaSTe
                                    29.11.2018 22:48

                                    Ну тогда это пункт 1: вы не смогли логику вынести на уровень типов. Вот пример, как это делается.

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


                                    1. VolCh
                                      29.11.2018 23:35

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


                                      1. PsyHaSTe
                                        30.11.2018 00:09

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


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

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

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

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

                                        P.S. к этому
                                        В логике типов ошибиться гораздо проще чем в простом императивном коде,

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


                                        1. Druu
                                          30.11.2018 04:25

                                          В логики типов ошибиться намного сложнее

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


                                          1. VolCh
                                            30.11.2018 11:12

                                            Ну вот как какой-то тип с ограничением типа value > 0 "поймёт", что я решил почему-то что слово "неотрицательный" эквивалентно слову "положительный"?


                                        1. VolCh
                                          30.11.2018 11:10
                                          -2

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

                                          Я не делаю особой разницы между запуском программы и её компиляцией в контексте выявления ошибок. Какая разница будут сыпаться ошибки при исполнении или при компиляции? Ну чисто в теории есть разница, наверное, в пользу выявления ошибок при компиляции, но на практике ошибки рантайма куда более информативны. По крайней мере JS vs TS.


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

                                          Мое мышление сформировано Pascal, С и C++ в то время когда JS-ом или PHP ещё не пахло.


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

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


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

                                          Вот в ограничениях ошибиться гораздо проще, а понять где ошибся гораздо сложнее. Опять же я равниваю исключительно JS и TS — не знаю что там с F#, но всегда был уверен, что он про ФП, а не про типизацию, типизация у него такая же как у С# — строгая статическая. И, кажется, мы по разному трактуем слово "ошибка" — мне почти без разницы вылезла ошибка в компайлтайме или в рантайме. Если вылезла — значит допущена 100% и её нужно исправлять.


                                          1. lgorSL
                                            30.11.2018 11:39

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

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


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


                                            Рефакторить более-менее большой проект на питоне или на java/scala — тоже огромная разница. В статических языках та же IDEA хорошо справляется и можно совершенно свободно менять что хочется. Если не справляется — я узнаю об этом во время компиляции и тут же поправлю. С питоном среда разработки от тех же разработчиков справляет плохо и банальное переименование переменной/поля оставляет в различных местах "сюрпризы" на будущее.


                                            1. VolCh
                                              30.11.2018 12:12

                                              Если разница огромна, то может быть имеет смысл. Мне такое почти не встречалось, если не считать SQL, но это вообще свой особый мир.


                                          1. ApeCoder
                                            30.11.2018 11:57

                                            Ну чисто в теории есть разница, наверное, в пользу выявления ошибок при компиляции, но на практике ошибки рантайма куда более информативны. По крайней мере JS vs TS

                                            Какое тестовое покрытие у вашего кода?


                                            1. VolCh
                                              30.11.2018 12:10

                                              Близкое к 100%. Отдельных тестов на типы нет, если что.


                                              1. ApeCoder
                                                30.11.2018 12:19

                                                А в фоне тесты гоняются (типа https://wallabyjs.com/ )


                                                1. VolCh
                                                  30.11.2018 12:21

                                                  На сохранение исходников. Автоматическое отключено.


                                                  1. ApeCoder
                                                    30.11.2018 12:29

                                                    И сколько они проходят? Просто контроль типов обычно делается фоном в процессе набора


                                                    1. VolCh
                                                      30.11.2018 12:31

                                                      Сравнимо со временем проверки TS на одном и том железе на схожих по объёму проектах.


                                              1. PsyHaSTe
                                                30.11.2018 12:22

                                                А ведь все тесты можно было бы поменять на типы, и только поднять качество кода… )


                                                1. VolCh
                                                  30.11.2018 12:29

                                                  Как вы типами проверите реакцию системы на невалидные данные, пришедшие с базы или от пользователя? Или как вы типами проверите сложный численный метод, принимающий число и возвращающий число? Или система типов позволяет что-то вроде type A = tan(sqrt(number)) писать?


                                                  1. PsyHaSTe
                                                    30.11.2018 12:46

                                                    Или система типов позволяет что-то вроде type A = tan(sqrt(number)) писать?

                                                    Позволяет, почему нет?


                                                    Как вы типами проверите реакцию системы на невалидные данные, пришедшие с базы или от пользователя?

                                                    Валидируете один раз IO, дальше все работает с точными данными.


                                                    1. VolCh
                                                      30.11.2018 12:54

                                                      Не закончил мысль, не просто позволяет ли, а даёт ли гарантии, что для пи в квадрате значение будет 0?


                                                      Валидировать-то я могу и без типов. Как без хаков типа явного приведения или тайпгвардов прийти от почти нетипизированного json к какому-нибудь Dictionary<EntityId,<Model<Entity>>>?


                                                      1. PsyHaSTe
                                                        30.11.2018 12:57

                                                        Не закончил мысль, не просто позволяет ли, а даёт ли гарантии, что для пи в квадрате значение будет 0?

                                                        При желании можно выразить. Я уже говорил, те же завтипы позволяют компилятору выводить утверждения в стиле "положительное число, делящееся на 3, от 14 до 25".


                                                        Валидировать-то я могу и без типов. Как без хаков типа явного приведения или тайпгвардов прийти от почти нетипизированного json к какому-нибудь Dictionary<EntityId,<Model>>?

                                                        В том, что в IO (точнее в I, O то нас не интересует, т.к. переход к более слабой форме представления всегда успешен) это 1% программы, а остальной код работает с конкретной структурой, где гарантированно есть поле User, где есть поле first_name, которое является валидной UTF8 строкой длиной до 30. А если это ВДРУГ нарушается, то оно не валится хрен пойми где, а отваливается на самой границе [не наш код / наш код], и сразу понятно, в чем проблема, потом что контекст еще не потерян.


                                          1. PsyHaSTe
                                            30.11.2018 12:21

                                            Я не делаю особой разницы между запуском программы и её компиляцией в контексте выявления ошибок. Какая разница будут сыпаться ошибки при исполнении или при компиляции? Ну чисто в теории есть разница, наверное, в пользу выявления ошибок при компиляции, но на практике ошибки рантайма куда более информативны. По крайней мере JS vs TS.

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


                                            1. VolCh
                                              30.11.2018 12:30

                                              Нет, конечно, как и не могу с помощью типов их все описать.


                                              1. PsyHaSTe
                                                30.11.2018 12:48

                                                Ну не знаю, люди как раз с помощью типов и описывают. Напрмер bar<T: Foo>() означает "вызвать bar для любых типов, которые имеют метод foo". И проверит, что во всевозможных вызовах метод действительно есть.


                                                1. VolCh
                                                  30.11.2018 12:55

                                                  А что он ожидаемо меняет состояние как проверить? На каждую область эквивалентности ожидаемого поведения свои типы где-то написать?


                                                  1. PsyHaSTe
                                                    30.11.2018 12:58

                                                    Ну например можно эффектами например описать. Тогда проверите, что он в БД лезет (или не лезет, смотря какие требования).


                                          1. KostaArnorsky
                                            30.11.2018 18:18

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


                                            1. VolCh
                                              30.11.2018 20:48

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


                          1. creker
                            29.11.2018 16:53

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

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


                            1. VolCh
                              29.11.2018 16:54

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


                              1. creker
                                29.11.2018 17:00

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


                                1. PsyHaSTe
                                  29.11.2018 17:02

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


                                1. VolCh
                                  29.11.2018 21:35

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


                                  1. VolCh
                                    29.11.2018 21:36

                                    Автодополнение


                                    1. PsyHaSTe
                                      29.11.2018 22:47

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


                                      1. VolCh
                                        29.11.2018 23:40

                                        А кто сказал, что без типов? IDE нынче хорошо выводят типы даже без явного их указания. А если им немного помочь парой аннотаций типа ` // var BaseClass|string $clasname` то и выражение типа `return new $clasname поймёт`.


                                        1. vintage
                                          30.11.2018 09:04
                                          -1

                                          без явного их указания

                                          ...


                                          var BaseClass|string $clasname

                                          Вас ничего не смущает?


                                          1. ApeCoder
                                            30.11.2018 09:12
                                            +3

                                            Меня смущает такое выборочное цитирование. Cмысл сообщения в том, что IDE и так хорошо справляются (без указания типов), но если им немного помочь (явно указать типы), то будут справляться лучше. У меня ощущение что вы просто не прочитали или не поняли большую часть того, на что отвечаете.


                                            1. vintage
                                              30.11.2018 11:10

                                              Компилятор тоже хорошо справляется с выводом типов, при этом ещё и услужливо скажет, где конкретно ему нужна помощь.


                                          1. VolCh
                                            30.11.2018 11:20

                                            Нет.


                                1. VolCh
                                  30.11.2018 11:15

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


                1. zagayevskiy
                  29.11.2018 11:24

                  Прекращайте уже писать С и С++ через /. Вы современный Си и современный Си++-то видели?


                  1. mayorovp
                    29.11.2018 12:00

                    По части ногострелов они близнецы-братья


                    1. zagayevskiy
                      29.11.2018 16:08
                      -1

                      Понятно. Не видели.


              1. eugenk
                28.11.2018 14:12

                Справедливости ради хорошие IDE нормально работают с (некоторыми) динамическими языками

                Какие например? Я использовал поделия от JetBrains для js и python. Говно-говном. Хотя JetBrains реальный монстр, у них пожалуй самые сильные алгоритмы анализа кода. Просто они не волшебники. И если типы не выводятся в принципе, абсолютно логично что они их вывести и не могут. Тут не JetBrains нужен, а Гендальф!
                А вообще со своим нынешним хобби-проектиком (надеюсь правда это продавать !) сижу сейчас на vs code и доволен как слон. Там у меня ts + php. Всё взаимно дружит и отлаживается в рамках одного проекта, тока в путь! :)) Хочу сейчас в добавок научиться писать для vs code свои расширения, как я это делаю для эклипса.


                1. VolCh
                  28.11.2018 16:16

                  JetBrains прежде всего, да. Особенно если им всякие *doc аннотации о типах скармливать там где они вывести не могут.


                  А для PHP тоже говно-говном? Концептуально он мало чем от JS отличается же с точки зрения статанализа: интерпретируемый язык со слабой динамической типизацией. И даже typehints по сути сахар для if (is_string($param)) throw new TypeError();


      1. Zoolander
        28.11.2018 01:54

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

        interface IData {
          title?: string,
          content?: string, 
          footer?: string, 
          onSubmit?: Function, 
          onDismiss?: Function
        }
        
        function createDialog(data: IData) {
          console.log(`data: ${data.title} ,  ${data.content}, ${data.footer}`)
        }
        
        createDialog({title:"Hello"});
        createDialog({title:"Hello Again", content:" Content Text"});
        
        /*
          в консоли будет
          data: Hello ,  undefined, undefined
          data: Hello Again ,   Content Text, undefined
         */


      1. Zoolander
        28.11.2018 01:58

        Кроме того, можно сократить писанину, используя интерфейсы, расширяющие интерфейсы

        interface IArticleData {
          title?: string,
          content?: string,
        }
        
        interface IArticleFullData extends IArticleData{
          footer?: string,
          onSubmit?: Function,
          onDismiss?: Function
        }
        
        function createDialog(data: IArticleFullData) {
          console.log(`data: ${data.title} ,  ${data.content}, ${data.footer}`)
        }


        IArticleFullData — это полный аналог IData из предыдущего примера


      1. 0xd34df00d
        28.11.2018 02:04

        Зачем что-то там перечислять ещё раз?


        data DialogBodyOptions = DialogBodyOptions
          { title :: String
          , content :: String
          , footer :: String
          } deriving (Eq, Ord, Show)
        
        data DialogOptions = DialogOptions
          { bodyOptions :: DialogBodyOptions
          , onSubmit :: Handler
          , onDismiss :: Handler
          } deriving (Eq, Ord, Show)
        
        createDialog :: DialogOptions -> SomeType
        createDialog DialogOptions { bodyOptions = DialogBodyOptions { .. }, .. } =
          тута всё в скоупе

        .. — часть синтаксиса, да.


        1. justboris
          28.11.2018 02:34
          +1

          Отвечу сразу и вам, и Zoolander

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

          Поэтому лучше держать эти типы отдельно и мириться с многословностью. Особенно, если учесть, что синтаксис в С# и Typescript более компактный, чем в Java. Может быть и там когда-нибудь сахар из Lombok включат в основу языка.


          1. 0xd34df00d
            28.11.2018 02:45

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

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


            1. justboris
              28.11.2018 02:57

              Утверждение "как только тайпчекер доволен, всё сразу работает" сильно переоценено.


              Допустим, у вас есть некоторый счетчик в коде


              var numberOfProcessedItems = 0;
              for(const item of items) {
                if(matchesCondition(item)) {
                   // тут много кода
                   numberOfProcessedItems++;
                }
              }

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


              var numberOfProcessedItems = 0;
              for(const item of items) {
                if(matchesCondition(item)) {
                   numberOfProcessedItems++; // раз
                   // тут много кода
                   numberOfProcessedItems++; // два!
                }
              }

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


              1. 0xd34df00d
                28.11.2018 03:27

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


                processItems items = something
                  where processedItems = func <$> filter matchesCondition items
                        numberOfProcessedItems = length processedItems

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


                Нет ручных инкрементов — нет повода для копипасты.


              1. areht
                28.11.2018 03:42

                В принципе, согласен с предыдущим комментарием, но…

                // тут много кода < — проблема тут

                Когда в IDE код легко поправить в

                var numberOfProcessedItems = 0;
                for(const item of items) {
                  if(matchesCondition(item)) {
                     ProcessItem(item);
                     numberOfProcessedItems++;
                  }
                }

                то и проблем, внезапно, не проявляется. У вас просто не будет правок кода тут — ни рефакторинга, ни проблемного мерджа.

                И проблема именно в IDE: когда рефакторинг делается в один клик и без ошибок — руки сами тянутся мусор спрятать. А когда мне приходится работать в блокноте — у меня тоже «много кода» появляется.

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

                А когда IDE не помогает — приходится себя убеждать даже от типов отказываться )


                1. justboris
                  28.11.2018 11:50

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

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


                  1. aikixd
                    28.11.2018 12:06
                    -1

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


                    1. Cerberuser
                      28.11.2018 12:26

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


                      1. aikixd
                        28.11.2018 13:19
                        -1

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


                        Вот наприме метод:


                        ApiResult<PostCommentResult> PostComment(PostCommentRequest request);

                        Как бы вы не хотели, вы обязательно верно интерпретируете аргумент и вернете верный результат. Вы не можете вернуть строку или WebResponse или что-еще. Если метод выше требует ApiResult то вы обязательно вызовете этот метод, потому что иначе нужный объект вам не получить. Сам факт того что кто-то требует этот тип уже подсказывает вам что нужно сделать.

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


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


                        1. mayorovp
                          28.11.2018 13:51

                          Зато этот метод может запросто вернуть константу, которая никак не зависит от входного аргумента — и ваша система типов никак это не отследит.


                          1. aikixd
                            28.11.2018 14:17

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


                            1. mayorovp
                              28.11.2018 14:42

                              Но вот это ваше утверждение остается неверным:


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


                              1. aikixd
                                28.11.2018 15:31

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


                                1. mayorovp
                                  28.11.2018 15:33

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


                                  1. aikixd
                                    28.11.2018 15:38

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


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


                                    1. mayorovp
                                      28.11.2018 15:40

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


                                      1. aikixd
                                        28.11.2018 15:48
                                        -1

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


                                        Так можно дойти до вопроса, почему типы не проверяют код на соответсвие с ТЗ.


                          1. Meloman19
                            28.11.2018 14:40

                            Ваша… Никакая система типов не отследит константу возвращает метод или нет. Это уже не зависит от системы типов в принципе.


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


                            1. impwx
                              28.11.2018 18:34

                              Константы отслеживать очень просто, для этого например в C++ есть модификатор constexpr.


                          1. 0xd34df00d
                            28.11.2018 22:46

                            Этот метод может вернуть константу (или пяток констант с банальным if'ом внутри), что и тесты не отследят. Что же делать-то?


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


                  1. areht
                    28.11.2018 13:11

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

                    А я хотел донести мысль, что сложность и бажность зависит не только от типов, а от инструментов и практик. Если инструменты под типизированный язык есть, а под нетипизированный нету, то баланс сильно смещается в сторону «я лучше var name = (string)json[»name"] напишу, чем с динамикой свяжусь".
                    А корреляция между типизацией языка и доступностью инструментов есть.


      1. Druu
        28.11.2018 04:38

        В типизированном подходе вам понадобится описать интерфейс DialogOptions и перечислить эти поля еще раз.

        Нет, можно написать вот прямо так как вы написали:


        function createDialog({title, content, footer, onSubmit, onDismiss, ...andMore}) {
        }

        и выведет автоматом тип: createDialog: { [x: string]: any, title: any, content: any, footer: any, onSubmit: any, onDismiss: any } -> void
        и потом можно написать createDialog({title: 1, content: 2, footer: 3, onSibmit: 4, foo: 6, bar: 7}) — ошибка, отсутствует onDismiss, createDialog({title: 1, content: 2, footer: 3, onSibmit: 4, onDismiss: 5, foo: 6, bar: 7}) — все хорошо.


        1. justboris
          28.11.2018 11:34
          +1

          Это будет не выведение типов, а implicit any, который отключается в strict-режиме Typescript. Именно возможность такого any где попало автор статьи и большинство комментаторов и называют происками Javascript в системе типов.


          А в strict-режиме в этом коде будет ошибка, вот такая. Надо будет либо поставить any руками, либо описать типы по-нормальному.


          1. Druu
            28.11.2018 11:44

            Это будет не выведение типов, а implicit any

            implicit any будет для конкретных полей. Но у аргумента ф-и тип — не any, его тип — объект с наличием конкретных полей


    1. taliban
      27.11.2018 21:46

      habr.com/post/431250/#comment_19422410 вот камент самый правильный, там же и другая сторона, можете ознакомиться, дядька умный рассказывает с многолетним опытом в разработке


  1. arty
    27.11.2018 20:11
    -3

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

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


    1. gBear
      27.11.2018 20:46
      +2

      А можно ссылочку там, или выходные данные на эти «научные исследования»? А тож оно сильно «в зависимости от» должно быть. В смысле, это же столько от типизации должно зависить, сколько от системы типов.


      1. arty
        27.11.2018 23:01
        +2

        да пожалуйста результаты от IBM: people.cs.aau.dk/~bnielsen/TOV08/material/tdd-practice.pdf


        1. khim
          28.11.2018 00:30
          +2

          Не увидел там сравнения статических и динамических языков от слова «совсем».

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

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


          1. arty
            28.11.2018 00:39
            -1

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


            1. khim
              28.11.2018 00:44
              +2

              тесты защищают от ошибок типов и ещё многих других ошибок
              Несуществующие тесты не защищают ни от каких ошибок.

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

              Ровно это, в общем-то утверждается в статье — и ровно это вы грозились опровергнуть.


              1. arty
                28.11.2018 00:47
                -2

                Несуществующие тесты не защищают ни от каких ошибок.

                зачем это здесь сказано? ситуацию отсутствия тестов здесь никто не упоминал до сих пор


                1. khim
                  28.11.2018 00:52
                  +1

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


                  1. arty
                    28.11.2018 00:54
                    -2

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


                    1. khim
                      28.11.2018 01:12

                      Смотрю:

                      Мы все отлично знаем, что защищенность через покрытие — иллюзия. Тесты пишут руками, они по определению менее надежны, чем встроенная в язык система проверки типов.
                      Перевожу с русского на русский: «защищенность через покрытие — иллюзия» == «никто не гарантирует вам, что вы покроете все случаи тестами». И ешё: «тесты пишут руками, они по определению менее надежны, чем встроенная в язык система проверки типов» — если таки прочитать всю статью целиком, а не видирать из неё пару слов, то можно будет заметить там такой пассаж: «Главный плюс статической типизации — гарантия. Если ты используешь ее в одном модуле, а в другом нет, ты просто потратил время и силы на описание и разработку типов, а никаких гарантий не получил.»

                      Откуда видно, что автор категорически против приёмов, которые позволяют «обмануть» тайпчекер — что указывает на то, что он видит преимущество TypeScript именно в том, что проверки типов покрывают весь код, в то время как тесты могут этого не делать. А не будут они этого делать только в одном случае — когда они не написаны.

                      Про остальные темы я не подписывался спорить.
                      Рекомендую всё-таки спорить со статьёй, а не воевать с ветряными мельницами…


                      1. arty
                        28.11.2018 01:19
                        -1

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

                        Рекомендую всё-таки спорить со статьёй, а не воевать с ветряными мельницами…

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


                        Переводить с русского на русский не нужно, каждый может перевести по-своему. Что написано, то и обсуждаем.


              1. arty
                28.11.2018 00:50
                -3

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

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


                1. khim
                  28.11.2018 01:05
                  +2

                  Для разнообразия сторонники сильной типизации могут поделиться научными исследованиями вместо возмущённых постов.
                  Вот, пожалуйста. Да, строгая типизация, сама по себе, панацеей не является (С/C++ несмотря на типизацию остаются самыми «опасными» языками… откуда и все попытки найти им замену), но если сравнить JavaScript и TypeScript — то разница очевидна.


                  1. arty
                    28.11.2018 01:10
                    -1

                    Вот, совсем другое дело!

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

                    Я же сослался на исследование, которое один и тот же проект без тестов и с тестами делал, и это гораздо более честное сравнение, на мой взгляд.


                    1. khim
                      28.11.2018 01:17

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

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


                      1. arty
                        28.11.2018 01:33

                        Ага, здесь как раз тот случай, когда два человека по-разному поняли написанное. Моё понимание отличается, потому что я ни разу не видел юнит тестов, которые только type errors и проверяют. Я видел во множестве тесты, которые проверяют логику, ну и заодно типы тоже. По-моему, так и нужно делать. И в такой ситуации проверки компилятором добавляют мало пользы.


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


                        1. khim
                          28.11.2018 01:55

                          Моё понимание отличается, потому что я ни разу не видел юнит тестов, которые только type errors и проверяют.
                          Ну дык это ещё хуже! Если вы проверили, что у вас всё нормально работает при задании какой-нибудь ставки в 3.0, 10.0 и 150.0 (проценты), но не проверили, что будет, если передать туда null или undefined (которые запросто могут вылезти из-за того, что свойство в базе или формочке пропало из-за рефакторинга), то вы оставляете в программе потенциальную уязвимость.

                          А назвать проверку на null «проверкой на логику»… мне кажется, что это перебор… это в чистом виде проверка на тип…


                  1. VolCh
                    28.11.2018 12:36

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


            1. 0xd34df00d
              28.11.2018 02:07

              система типов защищает только от ошибок типов

              Вы ведь знаете, что в достаточно мощных системах типов можно выражать в виде этих самых типов произвольные инварианты?


              1. arty
                28.11.2018 02:16

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


                1. 0xd34df00d
                  28.11.2018 02:35

                  Да. Я утверждаю, что ни одно из этих множеств не является подмножеством другого.


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


                  1. khim
                    28.11.2018 16:58

                    Как вы проверите, что < вообще порядок?
                    Ни в JavaScript, ни в PHP оператор сравнения ни разу не порядок, и оператор равенства — нифига не эквивалентность.


            1. AlexHell
              28.11.2018 02:48

              Вы же понимаете что чтобы написать эти тесты в JS (например) нужно приложить больше усилий и времени, на величину «проверки типов», по сравнению с вариантом «написать только тесты бизнес-логики» в варианте статической проверки типов.

              Во 2х, я бы сказал что защищают они даже не в момент рантайма программы, а в момент думанья человеком. Вот я допустим читаю вашу JS прогграмму, я должен перечитать тысячи (потенциально) тестовых вариантов чтобы понять какой тип аргумента у вас в функцию передается и может ли там быть null и должен ли там быть int от 1 до 10 вместо enum общепринятого. Верно?
              А еще мне документацию надо где-то отдельно от основного кода искать? Т.е не в коде я буду видеть
              Move[] GetMoves (TypeOfShape) — для нормального ООП
              а вот такое >>
              function GetMoves (TypeOfShape)
              причем тип возврата вообще не виден, и даже если задокументировать что вертает Move — вы уверены что это быстрей чем написать тип возврата? И тип аргумента?


            1. kagetoki
              28.11.2018 15:40

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


              Вы упускаете суть строгой статической типизации: она защищает от ошибок проектирования. Если система типов имеет еще и алгебраические типы данных, то при правильном проектировании она защищает от ошибок бизнес-логики. Это очень хорошо описано в книге Скотта Влашина "Domain Modeling Made Functional", настоятельно рекомендую.


              1. Aingis
                28.11.2018 16:08
                +1

                Даже интересно стало: как статическая типизация защитит от ошибок проектирования вроде той, что выбран алгоритм со сложностью O(n2) вместо O(n)?


                1. 0xd34df00d
                  28.11.2018 18:14

                  А как тесты от этого защитят?


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


                  1. JC_IIB
                    28.11.2018 18:43
                    +1

                    Ого. Дико интересно было бы взглянуть на реализацию.


                    1. 0xd34df00d
                      28.11.2018 23:04

                      Я пока ещё не садился и не думал, это так, по дороге на работу пораскинул мозгами.


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


                      Более прямой путь, но более трудоёмкий (ибо O-формализм говорит про асимптотику и поэтому, вообще говоря, неконструктивен, все соответствующие эпсилоны и дельты вас явно указывать никто не заставляет) — определить интересующую вас операцию особым образом. Пусть, скажем, вы хотите рассуждать о числе сравнений в сортировках. Тогда вместо cmp : t -> t -> Ordering вы выражаете алгоритм в терминах вроде cmp : Monad m => t -> t -> m Ordering (потому что сам факт сравнения в данном случае — это эффект, а эффект — это монада), и функцию сортировки как sort : Monad m => (T -> T -> m Ordering) -> List T -> m (List T), и рядом пишете доказательство вроде


                      data Counter : Type -> Type where
                        {- описание типа, реализующего монаду-счётчик -}
                      
                      CmpType : Type -> Type
                      CmpType t = t -> t -> Ordering
                      
                      countingCmp : (cmp : CmpType t) -> t -> t -> Counter Ordering
                      
                      comparisonsBound : Nat -> Nat
                      
                      sortComplexity : (cmp : CmpType t) ->
                                       (list : List t) ->
                                       (getCounter (sort (countingCmp cmp) list)
                                           <= countingCmp (length list))

                      Реализация функции sortComplexity будет доказательством, что функция сортировки делает не больше comparisonsBound n сравнений для списка длины n. Конечно, границу придётся определять точно, съехать на асимптотику не получится.
                      Параметричность вместе с тем гарантирует, что функция не залезет внутрь монады и не обнулит счётчик, например.
                      Подставляете вместо Counter, например, Identity-монаду — получаете обычную функцию сортировки. Подставляете Par — получаете параллелизм нахаляву.


                      Что-то такое, в общем.


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


                  1. sshikov
                    28.11.2018 19:37

                    Я боюсь, что это эквивалентно например проблеме останова.


                  1. Aingis
                    28.11.2018 22:25

                    Как раз тесты производительности давно придумали. Бывают даже стандартные постановки: «должно успевать за 10 мс и умещаться в 64 ГБ памяти» — в том числе на олимпиадах. Но почему вы переводите тему? Я про тесты ничего не говорил.


                    1. 0xd34df00d
                      28.11.2018 23:07

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


                      «Успевать за 10 мс и помещаться» ничего не говорит об асимптотике, кстати.


        1. gBear
          28.11.2018 11:41
          +1

          И где в это работе нашли, что

          защищённость через покрытие выше, чем ...
          ?!

          Она, мягко говоря, вообще про другое.


        1. transcengopher
          28.11.2018 13:21

          Но в этой статье не сравнивались покрытие тестами и сильная типизация, только сам факт наличия или отсутствия тестов (если я не прав, то укажите, пожалуйста, конкретное место в статье). По сути, статья является описанием опыта введения TDD в команду.

          Кстати, замечу, что в отсутствие строгой типизации упомянутых в статье 2930 юнит-тестов, 100 тестов производительности и >400 интеграционных тестов было бы явно недостаточно, так как пришлось бы писать ещё пару тысяч тестов на то, что DeviceFactory::createLightBulbDevice действительно возвращает нечто, что ведёт себя в соответствии контракту LightBulbDevice, а DeviceEvent::getType никогда не возвращает строку.


    1. sshikov
      27.11.2018 20:49

      Можно ссылочку на исследование?



    1. impwx
      27.11.2018 22:20

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


      1. arty
        27.11.2018 23:07
        +1

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


        1. khim
          28.11.2018 00:31

          А ведь ровно ссылочку на оправдание именно такого мнения от вас и просили…


          1. arty
            28.11.2018 00:33

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


            1. khim
              28.11.2018 00:39

              Это вы, кажется, их смешиваете. Потому что если «защищённость через покрытие выше, чем через сильную типизацию», то это значит, что мы можем взять любой язык без сильной типизации (JavaScript или там PHP), написать на нём тестов — и получить меньше ошибок, чем на C++, Java или, скажем, Rust'е.

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


              1. arty
                28.11.2018 00:42

                мои слова в другой ветке заодно отвечают и на этот комментарий habr.com/post/431250/?reply_to=19423678#comment_19423712


              1. arty
                28.11.2018 00:42

                а своё мнение про продолжение этой ветки я уже высказал


        1. Druu
          28.11.2018 04:52

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

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


        1. eugenk
          28.11.2018 06:48

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


        1. transcengopher
          28.11.2018 14:20

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

          1. тест на то, что при каждом вызове функции в неё будут переданы параметры именно того типа, который она ожидает.
          2. тест на то, что независимо от реализации функции, результатом её вызова будет нечто определённое.

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

          Приведу пример (я буду предполагать что система типов моего языка достаточно развита, чтобы всё это поддерживать):
          1. fun getLength — всё, что мы можем сказать о этой функции — это её имя. Исходя из нашего опыта, мы могли бы предположить, что она возвращает(get) некую длину(Length), но чего — мы не знаем. Также мы не знаем, что такое этот Length на самом деле. Нам нужны несколько групп тестов для каждого из типов, которые мы потенциально собираемся передать в эту функцию в качестве аргумента. Исходя, опять же, из опыта, мы предполагаем, что длина — это неотрицательное целое число. Нам нужны тесты, проверяющие, что функция действительно возвращает число, что оно действительно неотрицательное, и что оно действительно целое (минимум три типа тестов, для которых мы не гарантируем полного покрытия, ведь всегда останется шанс, что getLength("aaa") === 3, но getLength("™") === 3.14). Кроме того, нам нужны отдельные тесты на то, что функция не изменяет состояния своих параметров.
          2. fun getLength: List<Any> => Number — теперь нам уже не нужны вручную написанные проверки на принимаемый тип, ведь мы точно знаем, что если переданный параметр не является списком, то вызывающий код не пройдёт этап компиляции. Если также мы знаем, что базовый тип List не поддерживает мутации, то мы знаем, что вызов этой функции не меняет сам список. Нам всё ещё нужны проверки на то, что результат функции возвращает неотрицательное, и целое число.
          3. Пусть целое число у нас описывается типом Int. Тогда, если мы имеем
            fun getLength: List<Any> => Int — мы точно знаем, что длина всегда является целым числом, и каким бы ни было тело функции, она не сможет вернуть нецелый результат, или результат больше максимального или меньше минимального значения для Int. Но стоп, мы не знаем, что будет если передать в функцию Null или Undefined. Какова их длина? Нужен тест!
          4. Не совсем. Пусть в нашей системе типов, и Null, и Undefined являются отдельными типами. Так как наша функция определена как
            fun getLength: List<Any> => Int, она не содержит ни того, ни другого типа (Что выражалось бы, к примеру, как List<Any>|Null). Следовательно, никакой код не сможет передать внутрь Null, и никакая реализация не сможет вернуть Undefined. Потому этот класс тестов нам тоже не нужен. Нам по-прежнему нужны тесты на то, что результат неотрицательный.
          5. Если мы определили fun getLength: List<Any> => Length, а тип Length у нас:
            typedef Length as Int where $value >= 0 — то мы перекладываем ещё один из наших вручную написанных тестов на плечи компилятора, ведь теперь это его работа — проверять что функция не возвращает значений меньше нуля.

          Итого, из первоначальных классов тестов нам останется только проверить, что для списков предопределённой длины вызов getLength(list) вернёт строго ожидаемое значение. Так система типизации позволила нам писать тесты только одного типа вместо шести или семи, которые я обрисовал вначале, все остальные группы тестов «написаны» и выполнены компилятором.

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


          1. faiwer
            28.11.2018 14:35
            +1

            Я не думаю, что вам удастся найти живого JS\PHP\Ruby\Python программиста, кто будет писать все эти ваши тесты. Будет 1 тест вида:


            it('length', () =>
            {
              expect(getLength([1,2,3])).eq(3);
              expect(getLength([])).eq(0);
            });

            И всё.


            1. VolCh
              29.11.2018 16:31

              В целом, да. По крайней мере если нет явного требования типа "при передаче не массива выбрасывать TypeError" — тогда добавится ещё один.


          1. VolCh
            29.11.2018 16:34

            ведь теперь это его работа — проверять что функция не возвращает значений меньше нуля.

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


            1. mayorovp
              29.11.2018 16:44

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


              1. VolCh
                29.11.2018 16:58

                Вот как компилятор проверит, что программист проверил? Я навскидку могу с десяток способ проверки сделать. Ну, например, проверить старший бит хоть c XOR, хоть с AND маской. Или сравнить n === abs(n). Компилятор всё это учтёт?


                1. zagayevskiy
                  29.11.2018 20:17

                  Если компилятор вас в таком случае не поймет, то пока вы ему это не докажете, ничего не скомпилируется.


                1. Druu
                  30.11.2018 04:30

                  Вот как компилятор проверит, что программист проверил?

                  Ну делаете какой-то тип вида Either<Nat, BelowZero>, теперь чтобы со значением работать вам надо при помощи проверки выделить Nat.


                  Ну, например, проверить старший бит хоть c XOR, хоть с AND маской. Или сравнить n === abs(n). Компилятор всё это учтёт?

                  Компилятор освободит ваших коллег от необходимости писать вам замечание на ревью :)


            1. PsyHaSTe
              29.11.2018 16:54

              С теми же const generics мы можете Описать сигнатуру


              fn add_item<const N: usize>(a: [i32; N], b: i32) -> [i32; N + 1];

              И например такой код во время компиляции скажет unreachable code:


              let array = [1,2];
              let array2 = add_item(array, 3);
              if array2.len() == 2 {
                 ...
              }

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


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

              бтв не очень понял вопрос. Ну берется число из базы, что с ним не так? Делаете:


              let connection = establish_connection();
              let results = arrayLengthSettings
                  .limit(1)
                  .load::<ArrayLengthSetting>(&connection)
                  .expect("Error loading settings")?;
              let index: i32 = results[0].index;
              let index: usize = index.try_into()?;

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


        1. sshikov
          28.11.2018 19:39
          +1

          Стоп, стоп. С каких это пор тесты гарантируют отсутствие ошибок?


  1. Alexey2005
    27.11.2018 20:51
    +11

    До тех пор, пока программисты не научатся в обязательном порядке писать документацию, я буду убеждённым противником слабой динамической типизации.
    На самом деле это не так уж сложно. В конце концов, пользоваться репами вместо папочек привыкли, и тесты писать тоже как-то научились. Но вот мысль о том, что недокументированный код == несуществующий код для большинства программистов всё ещё является глубоко авангардистской.
    В итоге подавляющее большинство проектов не содержит вообще никакой документации кроме традиционной «шапки» с лицензией. Зачастую даже о зависимостях для успешной сборки приходится догадываться.
    И вот разбираться в таком коде, если он написан на чём-то динамически типизируемом — это просто адский ад. Вот например, прямо сейчас я пытаюсь разобраться в коде PDF.js, для которой разработчики конечно же не завезли вменяемых доков, а примеры покрывают от силы 2% возможностей библиотеки.
    И вот мне блин приходится гонять её под отладчиком, чтобы иметь возможность в рантайме проверить: что же, чёрт возьми, попадает в коллбэк при разрешении очередного промиса. Потому что из кода это как-то неочевидно.


    1. Alexey_Zal
      27.11.2018 21:31
      -1

      Как то я пытался разобраться с модулем lirc для Linux. Он был написан со статический типизацией и комментариями. Но мне это не помогло! В итоге всё всегда упирается в программиста. Мир не стоит на месте и разработка по это всегда компромисс между скоростью и качеством. И динамика, по моему опыту, даёт результаты быстрее. А если проект полетел то можно его потом причёсовать хоть до посинения. Один из первых применил это правило некто по фамилии Гейтс, и причёсывает своё По до сих пор.


      1. khim
        28.11.2018 00:33
        +1

        Один из первых применил это правило некто по фамилии Гейтс, и причёсывает своё По до сих пор.
        А в каком месте он это применил, извините? Basic писался на ассемблере, более поздние вещи на Pascal, C и тому подобных языках, ни о какой динамике ни в какой момент там речь не шла, насколько мне известно.


        1. Alexey_Zal
          28.11.2018 08:22

          Гейтс пожертвовал качеством кода в угоду скорости. Я об этом. И динамика в моем понимании — это жертва ради скорости разработки.


          1. PsyHaSTe
            28.11.2018 16:06

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


      1. Meloman19
        28.11.2018 02:29

        Как то я пытался разобраться с модулем lirc для Linux. Он был написан со статический типизацией и комментариями. Но мне это не помогло!
        А будь этот модуль написан с динамической типизацией всё сразу стало бы лучше?

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


        1. Alexey_Zal
          28.11.2018 08:21

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


          1. Meloman19
            28.11.2018 09:06

            Я вас сразу так и понял.


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


    1. eugenk
      28.11.2018 01:29

      Тут дело даже не в документации. А в том что приходится держать в голове массу деталей, которые при статике берёт на себя компилятор, а хорошая IDE с автокомплитом ещё и подсказывает. Я пытался писать на js. В итоге запутался на третьей тысяче строк кода. Хорошо добрые люди подсказали, что существует такая штука как ts (тогда он был совсем-совсем новым).


  1. eugenk
    28.11.2018 01:22
    +1

    Спасибо за статью. Что очень бы хотелось, это почитать мнение Вашего друга. Сам начинал с ассемблера. Казалось бы штука с типами и ООП вообще перпендикулярная. Однако всегда думал отталкиваясь от данных. Есть задача. Она работает с такими-то структурами данных. Их обрабатывают такие-то процедуры. И т.п. Фактически то же ООП, к которому перешел вполне безболезненно и естественно. Динамическую типизацию считаю Абсолютным Злом. Тем не менее вижу, какие сложные и нетривиальные библиотеки пишутся на том же js или на питоне. Из чего делаю вывод, что чего-то сильно не понимаю. Поэтому очень хотелось бы почитать статью подобную Вашей, но от сторонника динамической типизации. Пользоваться ей скорее всего никогда не буду (в конце концов мне уже под 60). Но просто интересно чисто с научной точки зрения. Может уговорите своего друга тут высказаться?


    1. movl
      28.11.2018 15:35
      +1

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

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

      > Динамическую типизацию считаю Абсолютным Злом

      И раз уж Вы допустили такое высказывание, то я допущу немного софистики. TypeScript стал популярен, во многом исходя из того, что был запрос на статическую типизацию для JS, и те кто пишет/писал на JS, в этой популяризации сыграли основную роль. Дальше совсем прикольно получается: выходит, что в целом, разработчики, которые принимают такие гибкие технологии как JavaScript, со всеми плюсами и минусами, готовы принять и нечто более строгое, такое как TypeScript, да впрочем и любые другие технологии и принципы: например функциональщина также очень распространена. А в обратную сторону, в случае разработчиков, вот таких как автор, сталкиваешься с какой-то древневековой косностью мышления и иррациональной верой в то, что всякому необходим код, который будет работать веками, а данные должны быть пропитаны десятками абстракций, якобы для какой-то еще более абстрактной «правильности» кода.


      1. PsyHaSTe
        28.11.2018 16:11

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

        Второе.


        Потому что все примеры, что я видел, легко разбиваются современными системами типов. "А вот надо много писать" — не надо, всё автоматически выводится. "А вот надо 2 разных типа вернуть" — не вопрос, вот вам ADT… Ну и так далее.


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


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


        1. justboris
          28.11.2018 16:39

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


          С другой стороны, если можно вывести типы без явного их указания, то может быть, можно то же самое сделать с Javascript, анализируя функции и как их вызывают? Бинго! Есть flow – запускаем этот инструмент поверх ванильного Javascript и получаем предупреждения о несовместимости типов. При желании можно и аннотации типов добавить, но это уже отступление от канона "не хочу писать никаких типов".


          1. IvanNochnoy
            28.11.2018 16:49

            Кстати, то же самое может и TypeScript: Type Checking JavaScript Files


          1. PsyHaSTe
            28.11.2018 18:04
            +1

            Прямо совсем все типы автоматически выводятся? Скорее всего нет, какие-то все равно придется описать. Вот так мы снова и приходим к аргументу «не хочу, не буду писать никаких типов», за который топят некоторые сторонники JS (не я).

            По месту использования — не нужно вообще. Ну вот пример на расте (Который типизирован выше крыши):


            fn send_internal(&self, method: Method, url: &str, body: Body) -> ResponseFuture {
                let uri = format!("https://api.telegram.org/{}", url);
                let request = Request::builder()
                    .method(method)
                    .uri(uri)
                    .header("Content-Type", "application/json")
                    .body(body)
                    .unwrap();
                self.client.request(request)
            }

            который на JS будет выглядеть как-то


            function send_internal(method, url, body)  {
                let uri = format("https://api.telegram.org/{}", url);
                let request = Request.builder()
                    .method(method)
                    .uri(uri)
                    .header("Content-Type", "application/json")
                    .body(body)
                return this.client.request(request);
            }

            Разницы почти нет.


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


            С другой стороны, если можно вывести типы без явного их указания, то может быть, можно то же самое сделать с Javascript, анализируя функции и как их вызывают? Бинго! Есть flow – запускаем этот инструмент поверх ванильного Javascript и получаем предупреждения о несовместимости типов. При желании можно и аннотации типов добавить, но это уже отступление от канона "не хочу писать никаких типов".

            Типы, они как прививки — если 100% кода ими покрыто, они очень действенны, если 95 — то уже поменьше, а если 90 и меньше — то уже ничего не спасет этот проект.


            image


        1. movl
          28.11.2018 17:11

          Ваш «очень узкий кейс» с «минорными плюсами», это и есть ключевая причина успешности интерпретируемых языков с динамической типизацией. Вспомните изначально для чего создавались такие языки как PHP, JavaScript, Bash и другие. То что они получили развитие, это мне кажется логичное явление после успеха. То что способ их применения мог значительно усложниться, это всего лишь некоторый этап развития, в следствие успеха. И например TypeScript, я считаю, достаточно естественным развитием некоторой технологии, которая внешне сильно ограничена (теми же браузерами), и в которой не хватает средств для построения больших абстрактных систем. Причем, я считаю, что TS появился слишком поздно, но если бы не он, то многие писали бы на каком-нибудь Haxe или Flow. У меня в голове и мысли нет противопоставлять различные способы типизации в категории: хорошо или плохо — этим я пытался показать абсурдность подобных обсуждений.

          Давайте так подытожим:

          Для того, чтобы загрузить с сервера некоторый текст и посчитать сколько раз в этом тексте встречается выражение «Hello, world!»: строгая статическое типизация — плохо, слабая динамическая — хорошо.

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

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


          1. PsyHaSTe
            28.11.2018 18:27

            Ваш «очень узкий кейс» с «минорными плюсами», это и есть ключевая причина успешности интерпретируемых языков с динамической типизацией. Вспомните изначально для чего создавались такие языки как PHP, JavaScript, Bash и другие. То что они получили развитие, это мне кажется логичное явление после успеха. То что способ их применения мог значительно усложниться, это всего лишь некоторый этап развития, в следствие успеха. И например TypeScript, я считаю, достаточно естественным развитием некоторой технологии, которая внешне сильно ограничена (теми же браузерами), и в которой не хватает средств для построения больших абстрактных систем. Причем, я считаю, что TS появился слишком поздно, но если бы не он, то многие писали бы на каком-нибудь Haxe или Flow. У меня в голове и мысли нет противопоставлять различные способы типизации в категории: хорошо или плохо — этим я пытался показать абсурдность подобных обсуждений.

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


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

            Я бы согласился, но буквально полгода назад был эксперимент, который мне было интересно провести: мы взяли небольшое JS-файл на 150 строк, который рисовал какую-то фигню, снежинки там и все такое. И переписали на TS. Переписывание заняло минут 30 (у человека, который TS в глаза не видел, я ему его рекламировал), и мы нашли 3 бага, в основном связанных с тем, что где-то записывались свойства, которых у объекта нет, а где-то они, наоборот, читались. На 50 строк кода была одна ошибка. Который в проде использовался больше года, и постоянно просматривался.




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


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


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


            1. VolCh
              29.11.2018 16:49

              А если уже оба инструмента знаешь (скажем, на момент изучения инструмента 2 инструмента 1 не было в принципе), то почему не выбирать инструмент по сценарию?


              1. PsyHaSTe
                29.11.2018 17:00

                Потому что я не вижу особого применения динамическим языкам. Как сказал товарищ в этом топике или соседнем, его рекорд: 1300 строк идеоматичного ООП кода в день. А больше 1000 строк это уже объем, где статика предпочтительнее.


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


                1. zagayevskiy
                  29.11.2018 20:25

                  Ну-ну, на этот «идиоматический ООП» ещё посмотреть надо. Там, ЕМНИП, речь была в контексте генерации кода. Может он там 1000 строк дата-классов нагенерил и радуется.


                1. VolCh
                  29.11.2018 23:17

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


                  1. PsyHaSTe
                    30.11.2018 00:12

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


      1. eugenk
        29.11.2018 07:36

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


        1. burfee
          29.11.2018 12:35

          Прихожу на проект. Там 10 индусов писали полгода. Оно разваливается. Прод через неделю. КАКИЕ ТУТ НАФИГ ТИПЫ?!?!))))
          Ну на js помогает линтер.

          Понятно что если есть время, перспектива и др. ресурсы, то лучше жить с типами. JS изначально был языком для «подхачить страничку быстренько». Сейчас все меняется, и пришли другие масштабы, на которых лучше жить с типами. Тем не менее в динамике тоже есть преимущество для некоторых оптимизаций, когда дженериков становится мало.


          1. impwx
            29.11.2018 13:39
            +1

            Прихожу на проект. Там 10 индусов писали полгода. Оно разваливается. Прод через неделю. КАКИЕ ТУТ НАФИГ ТИПЫ?!?!))))
            Прихожу на стройку. Там 10 индусов строили полгода. Оно разваливается. Сдавать через неделю. КАКОЙ ТУТ НАФИГ ФУНДАМЕНТ?
            в динамике тоже есть преимущество для некоторых оптимизаций
            А можно пример? Я предполагаю, что в языках с гибкой типизацией и макросами можно сделать то же самое, только как следует, а не monkey patching-ом.


        1. VolCh
          30.11.2018 11:24

          js js'у рознь. Я когда пишу на чистом js взял в привычку натравливать flow или ts на него, проверяя достаточно ли информации в коде для автовывода.


          1. Druu
            30.11.2018 12:56

            Я когда пишу на чистом js взял в привычку натравливать flow или ts на него, проверяя достаточно ли информации в коде для автовывода.

            А в чем проблема просто писать на flow или ts? Какие-то, честное слово, извращения у вас


            1. VolCh
              30.11.2018 12:57

              Придётся не игнорить там, где вышли ошибки типов.


    1. b1de0
      28.11.2018 16:55

      Проблема в том что у JS слабая динамическая типизация, а Python сильная (строгая) динамическая типизация. Это убирает в питоне много проблем, так как он не дает совершать действия которые не свойственные определенному типу.
      Приведу пример: JS — «Hello» + 123 = «Hello123»; Python — «Hello» + 123 = TypeError: must be str, not int.
      Мне кажется что(как многие писали выше) большинство вопросов у сторонников того же C# к JS связаны с слабой типизацией, а не динамической.
      P.S.: C++ имеет статичную но слабую типизацию.


  1. eugenk
    28.11.2018 01:36
    +1

    А вообще-то угораю конечно. Очень умные люди, разработчики движков, сушат мозги над тем, как из кода на js вытащить типы и использовать их для всяких хитровывернутых оптимизаций в виртуальной машине. Потом другие, тоже очень неглупые люди, создают такие штуки как typescript, scala.js и т.п, чтобы как-то подружить ту же скажем браузерную разработку с типами. Очень напоминает тот самый анекдот, где два ковбоя задаром говна поели :)))


  1. AlexHell
    28.11.2018 02:28
    +1

    Я начинал изучать программирование с StarCraft1 GUI, потом Warcraft3 (тоже с GUI) — там даже не код, а парадигма натаскивания / выбора из dropdown подходящего (ограниченного множества) — события и условия и действия.

    В техникуме на программера изучал JS — это был 1й язык именно написания кода (а не GUI).
    Потом был assembler :) круто показало внутрянку.
    И всегда на протяжении кодинга хоть на JS хоть на asm, я думал как с точки зрения сущностей / объектов… у нас был преподаватель на объектах чуть помешанный, ну там «очередь», «слот очереди», «животное», хотя и делали то на массивах параллельных, ну типа x[], y[] вместо объекта Obj {x, y}
    Т.е тут дело как это в голове уместить. И абстракции в духе «очередь» и «слот» и т.п. ложатся на мозг человека очень хорошо… уж не помню по каким результатам исследований, но чел не может в голове держать несвязанные разрозненные концепции — далеко не уедешь, надо пытаться свести к формальной модели с определенными входами и выходами, и разными уровнями абстракции… в том числе несколько абстракций нельзя держать, вроде как 7 шт и дале уже забываешь о других объектах…
    Если же исходить не из ООП объектов (которые по опыту можно и в JS и а ASM даже, главное концепция в голове), а строить какие-то сотни функций (function DoX(a, b, c) в JS) то в них очень просто запутаться… помню писал и шашки, и крестики-нолики, шахматы начинал — все на JS, по опыту знаю что путался… в итоге всеравно к объектам приходил, и к разным уровням абстракции, вроде это «поле» а это «фигура» а функция построения возможных ходов принимает «тип фигуры» в шахматах например… на Си без плюсов а потом и на C++ переписывал шахматы и явно было видно что статические типы облегчают концепции, в плане возврата в этому. Да это не Write-only, а когда недели/месяцы пилишь прогу (пусть и учебную в начале) то облегчают.
    А потом пришел к нормальному ООП в С++ и C# — это было круто и никогда не уйду от них в пользу динамических (уж не силен в терминологии, строгая там или нет и т.п свойства) как в JS.
    Да даже на статических и ООП заточенных типа C# бывает код абы-как написан, не используя нормальные общепринятые концепции… это как описывать кучу функций, кучу get-set или public свойства что по сути одно и тоже, но без адекватного поведения внутри объекта, и инкапсуляции… т.е добавить в объект свойство — это прошлый век (начало программирования), надо в него еще поведение заложить и инкапсулировать (чтоб все кому не лень не пытались извне копипастить, а если этот функционал в подчинении класса он и должен быть в нем описан — так понятней где искать — потом, при чтении кода и доработках)… имхо от недостатка опыта, они не допоняли концепции объектов со свойствами и поведением.

    Что до паттернов, которые типа унивесральные (даже к JS применимо есть паттерны), суть их в том чтобы другой чел (или ты через годы) вспомнил/понял как оно тут работает в соответствии с общепринятыми джентельменскими нормами… и не 100% программеров придерживаются, но они формально есть, все эти «State в динамических языках не нужны» от непонимания что есть самый State, т.е может у вас вся программа из иммутабельных объектов, а тут у вас есть класс который имеет внутренный вложенный private (или нет — в JS) класс и вот он мутабельный State т.е вы его там гоняете и собираете по колбекам (парсер какойто иерархии, да хоть XML/JSON/SVG для наглядности) и в итоге вот у вас StateOfParsingProcess — вот он формально есть. Даже если вы его не напишите в виде статически class StateOfParsingProcess и не обозначите private — он в вашей голове есть. Но вот напишете — потом вспомнить и вам же будет проще.

    Какие-то люди могут не понять как запрограммить процесс парсинга какогото формата файла на JS (не говорю про какие-то спец инструменты вроде разбора грамматик и построяния деревьев и т.п), вот они придумаю свою реализацию на любом языке, и она будет не понятна другому кто будет читать, потому что не то что не очевидна (это тоже), в ней может и логики не быть, потому что ее и не рефакторили, и сделали «чтоб работало» / write-only. А комуто это читать и допиливать вероятно.


  1. prijutme4ty
    28.11.2018 03:45

    У вас в голове дикая путаница.
    а) «Мой друг хотел делать наоборот — сразу писать код и не описывать никакие типы. Он не был готов определять проблему в виде семейства типов».
    «Это не значит, что динамически типизированные языки не нужны (на самом деле я и правда думаю, что не нужны). Это значит, что при их использовании, тебе нужно отказаться от ООП, как главенствующей парадигмы».
    «Канонические реализации большинства ООП паттернов пестрят кейвордом Interface, который совершенно бессмысленнен в динамически типизированном языке.»
    Вы очень узко смотрите на ООП. ООП — это не какая-то одна цельная парадигма, а очень широкий спектр различных вариантов ввести в язык объекты.
    Ну вот, например, вы сетуете на то, что в динамически типизированных языках интерфейсы не декларируются явным образом (хотя используются постоянно). А вы в курсе, что статически типизированный ООП-язык C++ не имеет интерфейсов вообще, и предлагает совершенно другие способы решения проблем?
    Динамические языки вполне себе описывают типы — возьмите Smalltalk и всех его последователей. Мне как человеку из ruby-мира с его тщательно продуманной системой типов просто смешно читать про то, что динамическая типизация — это отказ от продумывания пользовательских типов.
    JS — не лучший и далеко не самый типичный пример ООП-языка с динамической типизацией.
    б) «Паттерны — штука кросс-языковая». Эээ? Нет, паттерн — это всего-лишь типичное решение типичной проблемы. И в разных языках наборы задач и решений разные. Множество паттернов упростилось (вплоть до полного вырождения) с появлением в языках лямбда-функций, это же не повод поливать лямбды грязью и требовать возвращения всех тех сложностей, которые приходилось без них решать заведением специальных классов.
    в) «Главный плюс статической типизации — гарантия. Если ты используешь ее в одном модуле, а в другом нет, ты просто потратил время и силы на описание и разработку типов, а никаких гарантий не получил.»
    Часть модулей вашего кода получила гарантии согласованности. Если код настолько немодульный, что вы не получили никаких гарантий, то это просто плохой код. Статическая типизация его не исправит (скорее уж наоборот, законсервирует лишние взаимосвязи).
    в') Ага, а потом ваши гарантии разбиваются о какую-нибудь ковариантность коллекций, хе-хе.


    1. sshikov
      28.11.2018 19:51

      >Множество паттернов упростилось (вплоть до полного вырождения) с появлением в языках лямбда-функций

      Взамен появилось множество новых.


      1. prijutme4ty
        28.11.2018 19:53

        Это ли не аргумент против того, что "паттерны — штука кросс-языковая"?


        1. sshikov
          28.11.2018 20:07

          Ну… не уверен.

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

          С другой стороны, map/reduce/filter/etc — это вполне себе паттерн для многих языков, претендующих на функциональность, и выглядит практически одинаково в Scala и в JS, с точностью до типизации и ленивости (т.е. внешне одинаково, а работать конечно будет по-разному).


          1. Free_ze
            28.11.2018 20:56

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


            1. PsyHaSTe
              28.11.2018 21:18

              Потому что функия с паттерн матчингом — совершенно типичная ситуация в ФП, и не требует выделения целого паттерна.


              1. VolCh
                30.11.2018 11:26

                Условие было " функция не является примитивом 1 класса". если функции есть, а паттерн-матчинга нет?


                1. PsyHaSTe
                  30.11.2018 12:24

                  ФП без паттерн матчинга это что-то из разряда фантастики.


                  Собственно, не бывает ФП языков, бывает ФП стиль написания кода. И паттерн матчинг является одним из ключевых кирпичиков фундамента, без которого остальное имеет мало смысла.


                  1. VolCh
                    30.11.2018 12:32

                    Напомню, что мы не про ФП, а про языки где функция — примитив 1 класса.


            1. sshikov
              28.11.2018 21:22

              По большому счету любая фабрика — это вообще функция. С параметрами. Разве нет?

              >теряет актуальность
              Я немного не так сказал. Речь о том, что с появлением скажем лямбд, большинство паттернов, описанных в той самой книге GoF, становятся настолько тривиальными и очевидными, что по большей части уже не нужны. В частности, в виде отдельных классов. На примере Java 8 это очень ярко проявилось.


            1. mayorovp
              28.11.2018 21:43

              Паттерн называется не «фабрика», а «абстрактная фабрика», и состоит из как минимум двух классов: абстрактного класса фабрики и конкретной реализации.

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


              1. sshikov
                29.11.2018 20:50

                Вообще-то у GoF в книге две фабрики. И одна из них совсем не абстрактная.


                1. mayorovp
                  29.11.2018 21:44

                  Да, есть еще и Factory method. Но у него сильно сократилась область применимости, когда стали заменять наследование на композицию.

                  В любом случае, фабрика-функция — это отдельный паттерн.


  1. Druu
    28.11.2018 04:11

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

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


    1. eugenk
      28.11.2018 04:46

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


      1. Druu
        28.11.2018 05:07

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

        А вы точно на мой комментарий отвечали? Я не говорил, что динамика дает преимущества, я говорил, что на ней писать труднее, едва ли это можно назвать преимуществом :)


        Разговор о преимуществах вообще не совсем верный, правильнее говорить о возможностях. Статическая типизация дает возможность на уровне типов фиксировать инварианты и автоматически их проверять.
        Динамическая типизация сама по себе никаких возможностей не дает, т.к. это не фича, а ее отсутствие. Но косвенно она дает те возможности, которые плохо совместимы (или вообще несовместимы) с типами.
        Например — макросы с типами плохо совместимы (по-этому самые годные макросы — в динамических лиспах), рефлексия — плохо совместима (по-этому самая годная рефлексия — в каких-нибудь рубях и тех же лиспах, вроде CL), нормальный репл и image-based разработка (смоллтолк и снова лиспы) — фактически в рамках статики невозможны.


        1. eugenk
          28.11.2018 05:46

          А вы точно на мой комментарий отвечали? Я не говорил, что динамика дает преимущества, я говорил, что на ней писать труднее, едва ли это можно назвать преимуществом :)

          Прошу прощения, наверно не так выразился. Хотелось бы немного проникнуться методами, которые используются при таком подходе. Понять как это делается. Советовать мне что-либо написать самому, дело достаточно бесполезное. Ибо пишу уже очень давно и только на статически типизированных языках. Сами понимаете, что я напишу. С тем что Вы сказали, согласен лишь отчасти. Макросы например это те же шаблоны С++. Достаточно уродские. Но скажем в D эта штука уже вполне удобоваримая. Нормальный репл прекрасно живет в Scala или Haskell. И даже дружит с jupyter. Рефлексия хоть и не без бубнов, неплохо работает в java. Впрочем возможно опять Вас не понял. Честно говоря просто не могу себе представить концепций, невозможных в статике, но при этом практически полезных. Напоминаю однако, что это лишь моё мнение. Не претендующее на истину, а судя по популярности того же js от истины весьма далёкое. Вот и пытаюсь к истине как-то подобраться.


          1. 0xd34df00d
            28.11.2018 06:25

            Макросы например это те же шаблоны С++. Достаточно уродские.

            Это у вас просто макросов нормальных не было. Вроде хаскелевского Template Haskell, например.


            Нормальный репл прекрасно живет в Scala или Haskell.

            Про скалу не знаю, а в хаскеле он так себе. Жмёшь :reload, а все локальные байндинги пропадают.


            Да, есть костыли вокруг этого, но они всё равно костыли.


          1. Druu
            28.11.2018 09:08
            +1

            Макросы например это те же шаблоны С++.

            Просто нет.


            Нормальный репл прекрасно живет в Scala или Haskell.

            Что происходит в этом прекрасно живущем репле, когда вы сперва написали ф-ю f с типом Х, где-то ее поюзали в другой ф-и, а потом переопределили f так, что у нее тип стал Y?


            Рефлексия хоть и не без бубнов, неплохо работает в java.

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


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

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


            1. mayorovp
              28.11.2018 09:12

              Что происходит в этом прекрасно живущем репле, когда вы сперва написали ф-ю f с типом Х, где-то ее поюзали в другой ф-и, а потом переопределили f так, что у нее тип стал Y?

              Ошибка происходит: функция уже определена.


              1. Druu
                28.11.2018 09:22
                -1

                Ошибка происходит: функция уже определена.

                То есть, ваш репл невозможно использовать для image-based разработки. О чем и шла речь.


            1. 0xd34df00d
              28.11.2018 23:25

              Что происходит в этом прекрасно живущем репле, когда вы сперва написали ф-ю f с типом Х, где-то ее поюзали в другой ф-и, а потом переопределили f так, что у нее тип стал Y?

              Хаскелевский репл — это просто набор вложенных лет-байндингов, происходит что и ожидается:


              Prelude> f x = x * 2
              Prelude> g x = f $ f x
              Prelude> g 5
              20
              Prelude> f x = x <> "yay"
              Prelude> g 5
              20

              А что надо?


              1. Druu
                29.11.2018 03:32

                Надо иметь возможность переопределения старых ф-й (то есть в вашем примере в g должна быть вызвана вторая f). С-но это (переопределение ф-и в репле) основная операция при image-based разработке.


                1. 0xd34df00d
                  29.11.2018 08:02

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


        1. Cerberuser
          28.11.2018 08:12
          +1

          макросы с типами плохо совместимы

          Скажите это разработчикам на Rust — авторам Helix, к примеру :)


          1. Druu
            28.11.2018 09:34

            И макросы в расте чекаются после раскрытия. Вопрос — что происходит, когда потом в этом раскрытии ошибка типов?


        1. mayorovp
          28.11.2018 09:07

          Рефлексия с типами плохо совместима? Это вообще как?


          1. prijutme4ty
            28.11.2018 09:16

            Полагаю, имелась в виду не рефлексия, а более широко — метапрограммирование.


          1. Druu
            28.11.2018 09:16

            Рефлексия с типами плохо совместима? Это вообще как?

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


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


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


            1. mayorovp
              28.11.2018 09:29

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

              Вот вы пишите: «добавить в рантайме метод классу — что значение к списку». Класс — уже не тип? Тип. Так почему это рефлексия с типами плохо совместима-то?


              1. Druu
                28.11.2018 09:40

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

                Здесь есть некая доля путаницы, ага. Я подразумеваю под метапрограммированием работу с АСТ программы (макросы и другие варианты кодогенерации), а, с-но, рефлексия — это работа с программными объектами, которые этот АСТ может представлять. Если вы генерируете АСТ, который описывает класс — это метапрограммирование, если вы создаете программно некий класс как сущность, добавляете ему методы (как сущности) — то это рефлексия. Метапрограммирование может происходить при компиляции (к слову, тайпчек — формально одна из разновидностей метапрограммирования), рефлексия — в рантайме.


                Класс — уже не тип? Тип. Так почему это рефлексия с типами плохо совместима-то?

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


      1. prijutme4ty
        28.11.2018 10:07
        -1

        Попробуйте описать результат парсинга произвольного json в статически типизированном языке. Работая с такими объектами вы теряете все плюсы статически типизированного языка, ибо только в рантайме станет ясно, что вы ошиблись с типами входящих данных. В compile time вы не знаете, какого типа json[«name»], и даже не знаете, корректное ли это выражение.
        Чем динамический язык тут лучше? Вам не придется заводить обертки над значениями примитивных типов и руками распаковать их значения (а вам придется, если только у вас в языке нет union-type). Библиотека для работы с json в статически типизированном языке по сути будет реализовывать свою «динамическую типизацию».
        Кроме того, в динамическом языке вы спокойно можете писать литералы, соответствующие нужному вам объекту. Я не знаю, есть ли хоть один статически типизированный язык, который вам позволит такое.
        А задача работы с пришедшим извне json — типичнейшая.


        1. mayorovp
          28.11.2018 10:18

          С действительно произвольным json можно сделать не очень много полезных вещей, обычно все-таки программист знает схему пришедших данных. Эту самую схему можно или представить в виде типа данных (статического!), или держать в голове, но приводить типы по месту:


          var name = (string)json["name"];

          В любом случае система типов сработает как дополнительный слой защиты от некорректных данных и снизит вероятность появления ошибок подобных вот этой: https://habr.com/company/poiskvps/blog/422077/


          1. prijutme4ty
            28.11.2018 10:39

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

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

            Не очень понимаю, как проверка типов помогла бы в том примере (не работал с монгой и не знаю, что за тип у этих операторов и чем такой словарик будет отличаться от обычного {string=>string})?


            1. mayorovp
              28.11.2018 11:09

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

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


              Не очень понимаю, как проверка типов помогла бы в том примере

              Очень просто: если привести userInput.paramA к строке, то для {$empty: false} будет выброшено исключение, и никакой запрос к СУБД не уйдет.


            1. Druu
              28.11.2018 11:52
              +1

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

              Лень нажать кнопку и запустить автоматическую генерацию типов? Ну знаете ли...


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

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


              1. prijutme4ty
                28.11.2018 18:55

                Вы предлагаете кодогенерацию в качестве решения проблемы? Спасибо, но нет.


                1. PsyHaSTe
                  28.11.2018 19:11

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


                  image


                  Для примера на F#


                  let [<Literal>] questionsUrl = """https://api.stackexchange.com/2.2/questions?site=stackoverflow"""
                  
                  type Question = JsonProvider<questionsUrl>
                  let questions = Question.Load questionsUrl
                  questions.Items |> Array.iter (fun x -> printfn "%s" x.Title) 

                  Просто, понятно, читаемо, валидируется во время компиляции. Если вместо RetweetCount случайно написать RetwetCount, компилятор подчеркнет ошибку и ругнется. Удобно? Вроде, весьма. И не нужна никакая документация, потому что код + IDE сами по себе лучшая документация


                  image


                  1. TheShock
                    30.11.2018 01:50

                    А то получается, что сначала «типы обязательно нужно писать руками, а это долго»

                    Та даже если долго — лучше раз написать типы под API. Значительно дольше потом каждый раз стараться понять, что же в этой непонятной переменной `json`


                1. Druu
                  29.11.2018 03:37

                  Вы предлагаете кодогенерацию в качестве решения проблемы? Спасибо, но нет.

                  А в чем проблема? Легко, надежно.


            1. TheShock
              30.11.2018 01:41

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

              Вы какие-то ужасы рассказываете. О каком-то отвратительном апи с непонятной схемой. Как с такой вообще работать то? Что в динамическом, что в статическом? Все хорошие апи, которые я знаю очень легко описываются в статике. Да API, когда подключаешься к серверу — описывается в первую очередь, даже на чистом JS через JSDOC по той простой причине, что значительно легче посмотреть, что приходит в ответ на какую-то функцию в коде, чем запускать, ставить дебагер и пытаться поймать, что же там пришло.


        1. Cerberuser
          28.11.2018 10:18

          С пришедшим извне json неизвестного формата? Сказал бы, что, напротив, совершенно нетипичная и нежелательная, если бы сам с ней сейчас не возился (в попытках единообразно обрабатывать данные из нескольких таблиц БД). Но в общем случае, это же совершенно нормальная практика — требовать от JSON-а соответствия опеределённой схеме и выбрасывать ошибку (контролируемо, конечно, не по NPE), если пришла какая-нибудь ерунда.

          P.S. Минус не мой :)


          1. prijutme4ty
            28.11.2018 10:45

            Ок, у нас есть схема. И все равно, в чем профит-то по сравнению с динамически-типизированным языком? Вот вы потратили кучу сил на написание библиотеки, работающей с json, а дальше она не даёт вам никаких преимуществ статической типизации. И динамическая реализация, и статическая примерно одинаково выбросят ошибку, но на динамическую реализацию потрачено на два порядка меньше сил (на разработку библиотеки).


            1. mayorovp
              28.11.2018 11:10

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


            1. Cerberuser
              28.11.2018 11:33

              Это — одно проблемное место во всей кодовой базе. Объект, вышедший из-под JSON.parse<someInterface>, в дальнейшем обрабатывается с учётом всех статических типов. В то время как в динамически типизированном языке он так и останется "чем-то, определённым в рантайме".


              1. prijutme4ty
                28.11.2018 19:04

                Вероятно, у нас просто различаются задачи. У меня основной рабочий инструмент — скрипты и не слишком большие числомолотилки. Половина кода там будет тем самым проблемным местом, где статическая типизация бессильна, но тратит много сил на реализацию. Другая половина кода не настолько велика, чтобы нельзя было за время работы с ней не забыть, какого же типа объект передается в переменную с именем matrix.


                1. Cerberuser
                  29.11.2018 06:08

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


          1. KvanTTT
            28.11.2018 12:46

            С неизвестным форматом пример не очень удачный. Более удачный — это когда по типу JSON нужно динамически создать определенный объект. Такое используется при обработке AST.


        1. eugenk
          28.11.2018 14:31

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


        1. amarao
          28.11.2018 15:07

          enum {
          null,
          bool,
          int,
          str,
          float,
          list,
          object
          }

          В том же rust'е спокойно делается match по типу. И оно не динамически типизировано ни в один момент времени.

          см github.com/serde-rs/json


          1. prijutme4ty
            28.11.2018 18:53

            Я не спорю, что сделать это возможно, но там, где мне достаточно обратиться к элементу, вам дополнительно приходится громоздить type matching.


            1. PsyHaSTe
              28.11.2018 19:17

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


              1. prijutme4ty
                28.11.2018 19:22

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


            1. amarao
              29.11.2018 12:57

              Так в этом весь смысл. Если вы обращаетесь к элементу, которого не ожидаете? Например, вы умеете str, object, list, num, а прилетает null.


              1. prijutme4ty
                29.11.2018 13:00

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


                1. PsyHaSTe
                  29.11.2018 15:12
                  +1

                  Обычно это "точно знаю" заканчивается "блин, какой ДУРАК послал мне на вход ЭТО"? и "Почему у меня тут вместо строки [object Object]"


        1. 0xd34df00d
          28.11.2018 23:29

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

          Да хоть D-Bus.


    1. 0xd34df00d
      28.11.2018 06:23

      Человек, который умеет писать на динамике — прекрасно понимает, как использовать статику.

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


    1. elmm
      28.11.2018 11:42

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

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

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

      Есть языки которые и эти вопросы снимают по максимуму, но в моей сфере они не рапостранены.

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


      1. lgorSL
        28.11.2018 21:54
        +1

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

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


        Документация местами тоже божественная, например, "loss_func is loss function" — а с какими аргументами она будет вызвана, я сам должен угадать. При том что в нормальном языке просто в типах уже была бы информация о типах аргументов коллбека.


        И самое противное — мой изначально простой код типа load_image() обрастает такой же кучей проверок (каждая была добавлена после очередного падения в рантайме и поиска бага), потому что часть png картинок грузятся как трёхмерные массивы numpy с тремя или четырьмя каналами, иногда чёрно-белые картинки вместо этого грузятся как двухмерные массивы, а ещё иногда тип данных в массиве оказывается не uint8 с интервалом 0..255, а float с интервалом 0..1.


  1. EchoStan
    28.11.2018 05:47

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

    При этом использование JS или TS в итоге гораздо слабее влияет на результат (результатом считаем, естественно, поддерживаемость кода, а не производительность etc.), чем привычки архитектора. Кроме TS в арсенале программиста-хипстера есть статическая типизация в ODM и даже пропсы (во фронт-компонентах) с фиксированным типом.

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


  1. sairus777
    28.11.2018 06:12
    -1

    Julia — оптимальный вариант динамической + статической типизации. Можно типизировать только там, где надо.


    1. gatoazul
      28.11.2018 11:44

      И Perl6


  1. Tiendil
    28.11.2018 09:05

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


  1. istepan
    28.11.2018 09:29
    +1

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

    Сейчас задачи другие, и соответственно меняется подход.

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


  1. i360u
    28.11.2018 10:27

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


  1. jetcar
    28.11.2018 10:28
    -1

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


    1. kagetoki
      28.11.2018 14:46

      А для чего придуманы знаки препинания и заглавные буквы?


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


    1. Free_ze
      28.11.2018 14:57

      Вас никто не заставляет использовать AutoMapper. Проблема ведь не в языке.


  1. slonopotamus
    28.11.2018 10:42

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


    1. gatoazul
      28.11.2018 11:46

      Так оно и есть. Но к динамической типизации это не относится, и языком JS она не ограничивается.


    1. VolCh
      28.11.2018 12:48

      А PHP, который во многом похож на JS по отношению к типам, почему в современном мире используется?


      1. amironov
        28.11.2018 13:17

        Потому что бытует мнение (ошибочное), что PHP — это простой язык для начинающих.


  1. Mabusius
    28.11.2018 11:54

    Может я чего-то не понимаю, но я всегда думал, что динамическая типизация это когда 0 == '0' == false == null, а то о чем пишет автор это стили программирования.


    1. Cerberuser
      28.11.2018 12:06

      То, о чём пишете вы, — не динамическая, а слабая типизация. Динамическая — это когда можно написать let x = 1; processNumber(x); x = 'string'; processString(x); и не выворачивать себе мозг из-за того, что переменная x должна иметь разный тип в разных местах кода.


    1. mayorovp
      28.11.2018 12:06

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


      1. Mabusius
        28.11.2018 12:54

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


  1. kagetoki
    28.11.2018 12:44
    +1

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


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

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


    1. Kroid
      28.11.2018 18:20
      +1

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

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

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

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

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


      1. kagetoki
        29.11.2018 07:10

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

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

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

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

        Отдельно добавлю, что во многих мейнстримных языках (C#, Java, Go etc.) система типов слабая, не хватает банальных типов-сумм, я уже молчу про HKT. Тем не менее, это уже лучше, чем ничего.


        1. faiwer
          29.11.2018 08:12

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

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


          1. kagetoki
            29.11.2018 09:22

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


            1. faiwer
              29.11.2018 09:25
              -1

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


              1. kagetoki
                29.11.2018 09:49

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


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


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


                1. faiwer
                  29.11.2018 10:03

                  Ну у вас на одной стороне "там походу разберёмся", "не думать головой", "не выяснять заранее требования". А с другой те, кто набравшись опыта, и соответственно умея проектировать, пишут всё с первого раза на чистую. Ерунда какая-то. Чёрное и белое. Раздолбаи и гении.


                  Я веду речь о том, что, имхо, такое возможно только с хорошо знакомыми предметными областями. Либо с хорошо формализованными. А типовая ситуация как раз другая, и ключевые "неизменные" правила встают на уши, бизнес решает развернуться на 90 градусов посреди дороги, меняются приоритеты, неразумные сроки, и прочая чертовщина. Многие вещи становятся очевидными только уже в процессе реализации, когда все вынужденно погружаются в мельчайшие детали. И появляются все эти и многие другие проблемы. И гора legacy к тому же. Масштабный рефакторинг это вообще-то нормальное явление.


                  Т.е. так бывает не когда middle берётся за проектирование, это в целом рядовое явление. Чем опытнее архитектор и co, тем меньше будет граблей и серьёзных изменений\корректировок. Но они будут.


        1. Kroid
          29.11.2018 15:06
          +1

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

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


  1. gatoazul
    28.11.2018 12:51
    +1

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

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

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

    Поэтому весь спор напоминает дискуссии о том, что лучше — пиво или мороженое. Смотря кому и для чего.


    1. VolCh
      28.11.2018 13:09

      С другой стороны, вроде во многих популярных языках со статической типизацией есть или вводятся типы а-ля variant, any и т. п., а для динамических языков есть и улучшаются инструменты статического анализа.

      В общем предмет спора практически исчезает.


      1. gatoazul
        28.11.2018 13:26

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


    1. kagetoki
      28.11.2018 13:37
      +1

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


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

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


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

      Что такое универсальные приложения? Это когда и мессенджер, и календарь, и ядро ОС немношк?




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


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


    1. amarao
      28.11.2018 13:54

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

      Правильно — у нас есть много вариантов типов. И современные языки с алгебраическими типами данных не смотря на статическую типизацию отлично справляются. Более того, эти языки больно бьют по рукам того, кто забыл, что float — частично упорядочен, а в json'е может быть null.


  1. KvanTTT
    28.11.2018 12:58

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


  1. amarao
    28.11.2018 13:51

    Есть такое. Я не могу сказать про JS, но питон (который динамически типизированный) предлагает свои best practices как с этим жить. Де-факто статическая типизация питона происходит на этапе написания тестов, именно они проверяют, что у нас не будет «TypeError: cannot concatenate 'str' and 'NoneType' objects». И тесты на питоне из-за этого получаются очень избыточными, потому что приходится проверять буквально всё.

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


  1. ApeCoder
    28.11.2018 14:00

    Даже программируя на динамическом языке люди используют неявно статическую типизацию, так как для того, чтобы написать x.y(z) надо знать что у объекта x есть метод y, который принимает параметр z, а это и есть тип.


    1. amarao
      28.11.2018 14:44

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


      1. ApeCoder
        28.11.2018 14:58

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

        > У объекта может быть вообще враппер над rpc, который все вызовы всех методов тупо передаёт дальше.

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


        1. amarao
          28.11.2018 15:04

          На питоне не надо «знать». Просто получаешь getattr с именем, возвращаешь объект. Этот объект в себе уже содержит строку со своим названием, и все пришедшие аргументы вместе с этим названием пакуются в json — и в requests.

          Получается класс-прослойка, который готов выполнить и foo.len(), и foo.p_equal_np(), и foo.transpose(), абсолютно не зная про эти имена в момент написания кода.


          1. ApeCoder
            28.11.2018 15:34
            +1

            Я и говорю что прослойка не знает. Но в том месте где вы пишете foo.len() вам надо знать что при выполнении туда попадет что-то у чего есть метод len.


            1. amarao
              28.11.2018 15:39

              А, я понял вашу смысль. Мы ожидаем, что у «этого» есть длина. Это не типизация, это трейты, или, в контексте питона — «утиная типизация». Что угодно с ".len()" сойдёт в этом месте.

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


              1. mayorovp
                28.11.2018 15:41

                … и у этой функции тоже есть тип.


                1. amarao
                  28.11.2018 15:49

                  Наличие __call__, да. Но в этом случае, какой тип у mock.MagicMock? Оно может всё. Вообще всё.


                  1. mayorovp
                    28.11.2018 15:51

                    Указанный вами.


                    1. amarao
                      28.11.2018 16:44

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

                      — Прочитать её как флоат.
                      — Прочитать её как инт.
                      — Перейти на неё как на код.

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


                      1. mayorovp
                        28.11.2018 16:49

                        Память — и правда не типизирована. А mock.MagicMock — типизирован, но он типизирован тем типом, который вы подразумеваете. Моки же используются не потому что это круто, а в качестве замены для зависимостей. А у зависимостей есть типы.

                        Когда вы используете mock.MagicMock для замены foo.Bar — ваш mock.MagicMock как бы имеет тип foo.Bar. Код, которому вы передали свой мок, ожидает именно foo.Bar.


                        1. amarao
                          28.11.2018 16:52
                          -1

                          mock.MagicMock максимальное приближение к нетипизированному объекту, которое можно придумать. Он не "имеет тип", он не "вызывает ошибок". Ровно так же, как jmp $RANDOM не вызывает ошибок типизации в машинном коде (даже если это ахинея).


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


                  1. ApeCoder
                    28.11.2018 15:55

                    у него тип mock.MagicMock :) или mock.MagicMock — я не знаю питона глубоко


                    1. amarao
                      28.11.2018 16:47

                      С точки зрения системы типов у него типа нет, потому что он ничего не ограничивает.


                      1. ApeCoder
                        28.11.2018 17:11

                        Насколько я понял, он гарантировано предоставляет. Нет?


                        1. amarao
                          28.11.2018 17:18

                          Он их предоставляет в том смысле, что ничего не происходит. Ни хорошего, ни плохого.


                          1. ApeCoder
                            28.11.2018 17:30

                            Вот это наверное и есть гарантия — какой-нибудь int упадет с ошибкой


                            1. amarao
                              28.11.2018 17:53

                              Тыг это не гарантия, это анти-гарантия. Мы ожидаем, что на попытку str+None нас пошлют нафиг, а mock всё жрёт и причмокивает. Падают только негативные тесты (которые ожидают ошибку).

                              Повторю, magicMock, это такой метод отключить типизацию в питоне.


                              1. ApeCoder
                                28.11.2018 18:12

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


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


                                1. amarao
                                  28.11.2018 18:22

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

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

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

                                  Система типов — это соглашение об обозначении смыслов, плюс машинный энфорсинг этих соглашений. mock всё это отключает.


              1. ApeCoder
                28.11.2018 15:42

                "Это не типизация, это… — «утиная типизация». Что угодно с ".len()" сойдёт в этом месте.

                Утиная типизация это тоже типизация. Это просто не номинативная типизация а структурная.


              1. ApeCoder
                28.11.2018 15:45

                не думая о том, что это за функция.

                "функция" это уже тип.


                1. amarao
                  28.11.2018 15:50

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


                  1. ApeCoder
                    30.11.2018 12:10

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


    1. VolCh
      30.11.2018 12:08

      Или проверить наличие этого метода в рантайме, Или проверить тип в рантайме, когда значение придёт. Разве можно назвать статической типизацию параметра функции если пишешь код типа if (param instanceof SomeClass) throw new TypeError(), пускай он даже спрятан под сахаром типаfunction someFunction(SomeClass param)`?


  1. paratagas
    28.11.2018 14:01

    Я в последнее время пишу в основном на Javascript. Так что меня можно назвать сторонником слабой динамической типизации. Перечислю ее плюсы лично для себя, исходя из своего опыта…
    1. К примеру, у меня есть функция, которая парсит массив объектов, например Employee, и что-то с ними делает. В JS я могу описать логику этой функции, и затем применять ее к любому массиву объектов, чья внутреняя структура одинакова. Мне не нужно заботится о типе объектов, которые содержатся в массиве. В Typescript, насколько я понимаю, мне пришлось бы внутри моей функции явно указать тип объектов, которые содержатся в массиве. Что-то вроде ...<Employee[]> (прошу прощения, если ошибся в синтаксисе, никогда промышленно не писал на TS). При попытке передать туда массив объектов другого типа компилятор будет ругаться. Мне надо идти в мою функцию и переписывать ее, добавляя новый тип, или делая переключение типов, или создавая свой тип. Точно не помню, но суть — в усложнении возможностей повторного использования. Можно вообще присвоить параметру значение по умолчанию и пускай приходит любой тип. Если он будет расценен, как false, функция возьмет мое значение по умолчанию.
    Это же касается и возвращаемого функцией значения. В JS я могу вернуть в зависимости от логики функции, например, false, число или массив чисел. Мне не нужно прописывать 3 типа или выбирать только один их них. Причем возвращаемый результат я могу записывать с одну переменную. Например, присвоив ей при инициализации false, а затем число или массив чисел в зависимости, скажем, от количества возвращаемых данных. В других ЯП мне пришлось бы создавать 3 функции с одинаковыми именами, но с разной сигнатурой.
    2. Возможность быстрого сравнения данных разных типов. В частности, приведение к логическому типу. Например, если в метод пришла пустая строка, число 0, значение null или undefined, то все они логически определяются как false. Я могу просто проверить if(!value) и не делать ничего с пришедшими данными. Это позволяет быстро определять значения, непригодные для работы: undefined (если значение не было установлено), null (если явно установлено пустое значение) и какое-то из пустых числовых и строковых значений, трактующихся как false, не прибегая к определению типа этого значение. Мне просто не нужен тип в данным случае. Это также облегчает использование примитивов в параметрах функции. Я ожидаю в пераметре функции массив или строку, а приходит null. Я могу сделать быстрый возврат при получении null. Мне не нужно описывать тип каждого аргумента.
    3. Объединение значений разного типа. В JS я могу с легкостью сложить число со строкой. Это удобно при работе в WEB, например при получении данных их формы и произведения с ними каких-то вычислений. Например, я могу объединить имя (строка) и возраст (число) пользователя и вывести для него приветствие, например, «Здравствуйте, Петр. Сумма вашего заказа 30 руб.» Я как-то сталкивался с C# и искренне не мог понять, почему он не дает мне сложить число со строкой. Значения из полей форм в HTML приходят в string, и такая возможность бывает удобной. Это не камень в огород C#, просто я привык мыслить по-другому.
    4. Отдельно отмечу возможность логического сравнения в операторах && и ||. Например, один из излюбленных приемов среди JS-программистов:

    someFlagValue && anotherValue
    

    Позволяет вернуть, например, в шаблон второе значение, только если первое истинно.
    а
    firstValue || secondValue
    

    одно из истинных значений. Плюс в том, что мне не нужно думать, какой тип придет в них (undefined, null или пустая строка), для меня главное, как значение будет оцениваться логически.
    5. Не уверен точно, но мне кажется, что для создания массива в ЯП со строгой статической типизацией нужно, чтобы данные в массиве были одного типа. Часто это неудобно, особенно если нужно потом эти данные в качестве параметров функции в виде массива. В JS можно запихнуть в массив данные разных типов.
    6. Автоматическое приведение к числу. Это одна из моих любимых вещей. Посколько данные из HTML-форм приходят в программу в виде строк, даже числа, нет ничего странного, что в JS я могу проверить правильность
    '2' > '3'
    Например, когда у меня есть выпадающий список с перечнем годов и мне нужно сравнить 2 значения. В других ЯП часто нужно делать что-то вроде
    String.Compare(str1,str2)
    или
    ParseInt

    7. Устойчивость программы. Javascript позволяет гибко производить вычисления с данными разных типов. Да, эти вычисления могут быть ошибочными, но программа продолжит работу. Разумееется, это неприемлемо в ПО, предназначенном, скажем, для медицины и космоса, но вполне допустимо для показа на карте 10 ближайших баров в округе. Да, может быть, где-то будут ошибки с точными координатами или в названии одного из баров затесается "[object Object]", но приложение продолжит работу, что в данной ситуации, возможно, более важно.

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


    1. Aingis
      28.11.2018 14:30
      +1

      '2' > '3'
      будет сравнивать по алфавиту ровно как и другие подобные сравнения срок вида 'a' > 'b'. К числу здесь будет приводится, только в случае если один из аргументов — число ('2' > 11 — false, но '2' > '11' — true).
      В остальном полностью согласен.


    1. mayorovp
      28.11.2018 14:45

      Ответил вам ниже: https://habr.com/post/431250/#comment_19426588


    1. Druu
      28.11.2018 14:50
      +1

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

      Нет, не пришлось бы. Есть генерики, то есть пишите что-то вроде: parse(arr: T[]) {...} и теперь оно работает для любого Т.

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

      Прекрасно работает:


      function yoba(x: number) {
          if (x < 0) {
              return true;
          } else if (x === 0) {
              return 0;
          } else {
              return [''];
          }
      }
      
      let res = yoba(-10);
      res = 0;
      res = ['1', '2'];

      тип res: number | boolean | string[]


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

      function yoba(strs: string[] | null) {
          if (strs) {
              return strs.reduce((x, y) => x + y, '');
          } else {
              return '';
          }
      }

      В JS я могу с легкостью сложить число со строкой.

      Да не проблема:


      function yoba(x: string, y: number) {
          return x + y;
      }

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

      И не думайте!


      function yoba(x: undefined | null | boolean | string) {
          return x || 'yoba';
      }

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

      const yoba = [1, true, 'yoba'];

      yoba: (number | boolean | string)[]


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

      const yoba = '1' > '2';

      yoba: boolean


      Устойчивость программы. Javascript позволяет гибко производить вычисления с данными разных типов.

      типы не меняют семантику рантайма


      1. paratagas
        28.11.2018 15:08

        function yoba(x: undefined | null | boolean | string) {...

        Не уверен, что приведенный вами пример обладает хорошей читаемостью
        Все-таки
        function yoba(x = 'Default') {...
        воспринимается куда легче

        const yoba = [1, true, 'yoba'];

        Это также код и на JS. Не думаю, что C# или Java позволят такое напрямую без создания объектных оболочек.


        1. mayorovp
          28.11.2018 15:14

          Не думаю, что C# или Java позволят такое напрямую без создания объектных оболочек.

          В вашем ecmascript любое число уже изначально обладает своей объектной оболочкой! Так что неявное их создание компиляторов никак не может быть записано в недостатки C#.


          1. paratagas
            28.11.2018 15:26

            В вашем ecmascript любое число уже изначально обладает своей объектной оболочкой! Так что неявное их создание компиляторов никак не может быть записано в недостатки C#.
            Да, но для программиста при разработке тип примитива не будет является объектом с точки зрения языка. Это позволяет писать более лаконичный код. Разумеется, в JS можно написать код вида
            const myNum = new Number(2);
            Но тогда «myNum » явно будет объектом, а можно использовать сразу тип «number».


            1. mayorovp
              28.11.2018 15:29

              … и что? Вопрос-то был про массив из разнородных данных.


              1. paratagas
                28.11.2018 15:32

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

                const yoba = [1, true, 'yoba'];

                аналог кода на C# в виде массива из разнородных данных.


                1. mayorovp
                  28.11.2018 15:33

                  var yoba = new object[] { 1, true, "yoba" };


                  1. paratagas
                    28.11.2018 15:39

                    А «yoba» в вашем случае будет объектом или массивом?



                    1. Druu
                      28.11.2018 15:44

                      В C# массивы являются объектами.


        1. Druu
          28.11.2018 15:19

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

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


          function yoba(x = 'Default') {
              return x;
          }

          yoba: string? -> string


          Это также код и на JS.

          Это ts, он статически типизирован, все ок. Да, в c# или java так нельзя, потому что система типов в этих языках проектировалась несколько с другим прицелом.


          1. mayorovp
            28.11.2018 15:21

            Да, в c# или java так нельзя

            Да все можно же: var yoba = new object[] { 1, true, "yoba" };


            1. Druu
              28.11.2018 15:25

              Да, в c# или java так нельзя

              Ну да, с кастом до object можно, но это наверное все же несколько другой кейс. С-но, в тс можно типизировать и как const x: [number, boolean, string] = [1, true, '']; в c# будут таплы ближайшим аналогом (с тем исключением что таплы не являются подтипами массивов), есть ли что-то похожее в джаве — хз.


            1. paratagas
              28.11.2018 15:36

              А «yoba» в вашем случае будет объектом или массивом?


              1. mayorovp
                28.11.2018 15:38

                В первую очередь массивом. Объектом тоже будет, ведь любой массив — объект.


                1. paratagas
                  28.11.2018 15:41
                  -1

                  Простите, но простое гугление выдало мне такой способ создания массива в C#:

                  int[] numbers = new int[] { 1, 2, 3, 4, 5 };

                  Ваш пример больше похож на создание именно объекта.


                  1. mayorovp
                    28.11.2018 15:44

                    Простите, но вы несете чушь. Признак массива — [] (квадратные скобки), а вовсе не int. Вместо int можно написать любой тип данных. В том числе object. В моем коде написано object[], что определяет массив объектов.


                    1. paratagas
                      28.11.2018 15:59

                      что определяет массив объектов.
                      Но я просил привести пример на C# не массива объектов, а массива, в котором содержатся несколько значений с разными примитивными типами данных.


                      1. mayorovp
                        28.11.2018 16:00

                        Ну так в массиве объектов как раз и содержатся "несколько значений с примитивных типом данных". Что вам не нравится-то?


                        1. paratagas
                          28.11.2018 16:09

                          Тем, что внутри вашего массива лежит объект, который при итерации выдаст в качестве item именно объект. В моем примере на JS при итерации, скажем, с использованием «forEach» вы получаете доступ сразу к нужному значению массива. Также мне хотелось бы узнать, сможете ли вы передать созданный вами массив в функцию в качестве параметра?


                          1. mayorovp
                            28.11.2018 16:22

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


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

                            А что, собственно, может этому помешать?


                          1. Druu
                            28.11.2018 16:26

                            Тем, что внутри вашего массива лежит объект, который при итерации выдаст в качестве item именно объект. В моем примере на JS при итерации, скажем, с использованием «forEach» вы получаете доступ сразу к нужному значению массива.

                            В его примере точно так же будет доступ к нужному значению массива.


                            1. paratagas
                              28.11.2018 17:00

                              И этим значением будет объект, а не примитив. И уже из объекта нужно доставать значение, обращаясь к его свойству. Это усложняет структуру и понимание кода.


                              1. mayorovp
                                28.11.2018 17:01

                                Ну и где вы тут видите обращение к свойству объекта?


                                var x = (int)y;


                      1. Free_ze
                        28.11.2018 16:06

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

                        Массив содержит данные одного типа (и его подтипов). То, о чем вы говорите, является кортежем.


                        1. paratagas
                          28.11.2018 16:15

                          Массив содержит данные одного типа (и его подтипов). То, о чем вы говорите, является кортежем.

                          Спасибо! Вот то, что я имел ввиду. Сказываются различия в терминологии в ЯП)) Значит мое утверждение под номером 5 выше, по крайней мере, для C# неактуально.


          1. paratagas
            28.11.2018 15:35

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

            Вам для того, что в TS это заработало, пришлось перечислить все типы для x, которые могут прийти в функцию:
            function yoba(x: undefined | null | boolean | string) {...

            Я лишь подчеркнул, что JS этого не требует.


            1. Druu
              28.11.2018 15:50

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

              Так это два разных кода. Если вы на js напишете function yoba(x = 'Default') { то обрабатывать дефолтным образом оно будет только undefined, в точности как вариант на TS, null, false и все остальное потребуется описать руками.
              Ну а если вам просто лень писать | null | undefined… то можете просто сделать:


              type Falsey<T> = T | null | undefined | false | '' | 0;

              и использовать везде.


              1. paratagas
                28.11.2018 15:56
                -1

                Ну а если вам просто лень писать | null | undefined… то можете просто сделать

                А чем это тогда отличается от того, что есть в JS по умолчанию?


                1. mayorovp
                  28.11.2018 15:57

                  Неужели тем, что компилятор проверит корректность типов в вашем коде статически?


                  1. paratagas
                    28.11.2018 16:00

                    Но если там перечислены практически все типы данных, какой вообще тогда смысл в такой проверке?


                    1. Druu
                      28.11.2018 16:11
                      +1

                      Статика поможет тем, что вы для аргумента x типа Falsey гарантированно не забудете сделать проверку if (x) {} else {} и обработать граничный случай. А вот если типов нет — можете и забыть.


    1. areht
      28.11.2018 15:14

      1) То есть функция принимает массив чего-то, делает что-то и возвращает то ли строку, то ли массив, то ли bool? Как вы вообще понимаете что у вас в коде происходит?


      1. paratagas
        28.11.2018 15:29

        1) То есть функция принимает массив чего-то, делает что-то и возвращает то ли строку, то ли массив, то ли bool? Как вы вообще понимаете что у вас в коде происходит?

        Да, например, если нужное значение не вычислено или данных не хватает, то можно вернуть «false». Если нужно вернуть одно число, то оно и возвращается, если чисел больше одного, что можно вернуть массив.


        1. Free_ze
          28.11.2018 15:33

          Очень сложно придумать кейс, когда действительно нужна такая вариативность и нет нарушения SRP.

          «Because I can»


          1. Druu
            28.11.2018 15:39

            Вообще тут вопрос в том, как следует обрабатывать данные. Если все кейзы обрабатываются однородно, то имеет смысл возвращать пустой массив вместо null и массив из одного элемента вместо числа. С другой стороны, если эти случаи на call site требуется обработать специфически — лучше как раз сделать тип number[] | null | number — тогда мы гарантированно не забудем обработать кейзы с пустым и одинарным значением, в то время как в случае возвращаемого типа number[] их можно прощелкать.


            1. Free_ze
              28.11.2018 15:47

              если эти случаи на call site требуется обработать специфически

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


              1. Druu
                28.11.2018 15:56

                Специфика использования лежит вне зоны ответственности библиотечной функции.

                Способ обработки зависит от данных. А тип — фиксирует соответствующие инварианты
                Если данные логически предполагают определенный способ обработки — то они предполагают. Если перестают предполагать — это уже другие данные другого типа, что логично.


                1. Free_ze
                  28.11.2018 16:01

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


                  1. Druu
                    28.11.2018 16:06

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


          1. paratagas
            28.11.2018 15:49

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


            1. Free_ze
              28.11.2018 15:55
              -1

              К сожалению не удивит, это популярный подход в JS.


              1. paratagas
                28.11.2018 16:02

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


                1. ApeCoder
                  28.11.2018 16:09
                  +1

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


                1. Free_ze
                  28.11.2018 16:11

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


                  1. paratagas
                    28.11.2018 16:22
                    -1

                    Гибкие функции — это нарушение SRP

                    Так SRP это подход объектно-ориентированного, но не функционального программирования.


                    1. Free_ze
                      28.11.2018 16:26

                      Функциональное программирование поощряет божественные сущности?)


                      1. paratagas
                        28.11.2018 16:35
                        -1

                        Почему «божественные сущности»? Можно, например, в зависимости от переданных параметров вернуть из функции «а» созданную функцию «b», можно вернуть функцию «с», а можно вернуть «false», если что-то пошло не так.


                        1. Free_ze
                          28.11.2018 16:42

                          Почему «божественные сущности»?

                          Потому что слишком много ответственности на себя берут. «Вокруг них все крутится».

                          Можно, например, в зависимости от переданных параметров

                          Функции могут быть с одинаковой сигнатурой (иначе как потом узнать способ их вызова?), вместо false возвращать null, например.


                          1. paratagas
                            28.11.2018 17:06

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


                            1. mayorovp
                              28.11.2018 17:07

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


                              1. lega
                                28.11.2018 18:52

                                Надо вбросить, как она помогает?

                                в проектах совсем мало ошибок, которые уберёт статическая типизация
                                отсюда.


                                1. mayorovp
                                  28.11.2018 19:30

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


                                  1. lega
                                    28.11.2018 20:42

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


                            1. Free_ze
                              28.11.2018 17:16

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

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


                      1. IvanNochnoy
                        28.11.2018 16:40

                        Функциональное программирование не против Discriminated unions, а это почти то же, что в вышеуказанном примере.


                    1. zagayevskiy
                      29.11.2018 16:25

                      SRP это общий принцип программирования. Может(и должен) применяться в любой парадигме.


                  1. VolCh
                    30.11.2018 12:20

                    вы реально хотите с десяток вариаций console.log?


                    1. Free_ze
                      30.11.2018 12:27

                      О каких вариациях речь?


                      1. VolCh
                        30.11.2018 12:33

                        для каждого примитивного типа данных. для number, для string и т. п.


                        1. Free_ze
                          30.11.2018 12:47
                          +1

                          Зачем? Алгоритм console.log остается одним и тем же для разных типов данных. Но если он захочет для number варить кофе и возвращать промис готовности — вот это мы оттуда должны убрать.


                          1. PsyHaSTe
                            30.11.2018 12:49

                            Не обязательно, можно будет просто сделать тип-сумму.


                            Хотя запашок у подобной потребности определнный есть.


                            1. Free_ze
                              30.11.2018 12:55

                              SRP говорит, что не стоит так делать. Но чувствительность к запахам — штука индивидуальная)


                              1. PsyHaSTe
                                30.11.2018 12:59

                                Я верю, что так делать никогда не надо. Вопрос про "можем"/"не можем"))


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


                                1. Free_ze
                                  30.11.2018 13:06

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


                          1. VolCh
                            30.11.2018 12:56

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


                            1. PsyHaSTe
                              30.11.2018 12:59

                              динамическая диспетчеризация в помощь.


                              1. VolCh
                                30.11.2018 13:00

                                То есть проверка типов в рантайме?


                                1. PsyHaSTe
                                  30.11.2018 13:27

                                  С чего бы это?


                                  1. VolCh
                                    30.11.2018 17:13

                                    Ну а как реализуются разные ветви в зависимости от типа аргумента функции?


                                    1. PsyHaSTe
                                      30.11.2018 17:15

                                      С помощью таблицы виртуальных методов. Метод по смещению 10 от начала vtable — нужная нам версия foo.


                                      1. VolCh
                                        30.11.2018 17:16

                                        А как определить 10 или 0, если мы не знаем какого типа будет аргумент?


                                        1. PsyHaSTe
                                          30.11.2018 19:37

                                          Потому что vtable всегда имеет одни и те же методы по одному и тому же смещению. Почитайте как ООП работает, пожалуйста.


                                          1. VolCh
                                            30.11.2018 20:51

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


                                            1. PsyHaSTe
                                              30.11.2018 20:57

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

                                              И это не отменяет сказанного мной ни в какой мере.


                                    1. Free_ze
                                      30.11.2018 17:24

                                      Если член не обнаружен в текущем объекте, рантайм пытается получить объект прототипа (по ссылке, которая есть почти в каждом JS-объекте) и искать на нем, проходя так всю цепочку, пока либо не найдет искомое, либо не встретит null на месте ссылки.


                            1. Free_ze
                              30.11.2018 13:01

                              Преобразование сущностей к строке обеспечивает полиморфизм Object.toString, а console.log лишь отправляет результат на консоль.


                              1. vintage
                                30.11.2018 19:05
                                +2

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


                1. PsyHaSTe
                  28.11.2018 19:05

                  А потом сидишь с коллстеком в 50 "супер-гибких функций", и пытаешься понять, кто данные попортил, а возможно и не один раз. Каждая ведь решила "гибко" адаптироваться. И дебажишь 4 часа, чтобы понять, что в от в этом параметре число нужно в строковом представлении передавать, а не в числовом.


                1. zagayevskiy
                  29.11.2018 16:25
                  +1

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


          1. VolCh
            30.11.2018 12:18
            -1

            Аналог стандартного console.log


        1. areht
          28.11.2018 16:41
          +1

          «Можно вернуть» — это замечательно. Я даже не спрашиваю «зачем?» — пусть будет.

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


          1. paratagas
            28.11.2018 17:24

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


            1. ApeCoder
              28.11.2018 17:34

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


              При статической типизации вам это поможет сделать анализ типов и рефакторинги.


            1. areht
              28.11.2018 17:43
              +1

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

              > и не уронить всё приложение.

              Вы просто не заметите, ибо п.7


      1. Kroid
        28.11.2018 18:37

        Допустим, вам надо отправить сообщение юзеру, для этого нужно получить канал отправки по id юзера, а потом отправить это сообщение по каналу. У вас есть функция, которая принимает на вход user_id, на выходе отдает один из нескольких типов данных: или экземпляр класса SMS, или экземпляр класса Email, или экземпляр класса Websocket, или вообще null. Вы вызываете эту функцию, получаете объект, а потом через него отправляете сообщение object.send(«hello»).

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


        1. khim
          28.11.2018 21:44

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

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


          1. Kroid
            29.11.2018 00:39

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

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

            Вот и получается что-то такое странное:

            let arr = returnedDataFromBackend;
            arr.forEach((ele) => {
             // функция каждый раз возвращает переменную ele разных типов
             if (typeof ele === 'string') return renderString(ele);
             if (ele.type == 'image') return renderImage(ele);
             ...
            });
            

            Разумеется, этот код можно улучшить. К примеру, подойти к беку и сказать, чтобы он отдавал мне все данные в виде массива хешей с двумя полями: type и value. Либо самому написать прослойку которая нормализует полученные данные, а потом передает их дальше. Но это уже совсем другая история :)


            1. khim
              29.11.2018 00:52

              Если у меня фиксированное количество вариантов, то я либо должен сделать либо std::variant, либо банальный union с селектором, как в Pascal 1970го года. Либо можно сделать интерфейс, который будет с этим типом-содержащим-невесть-что работать (и потом его реализовать).

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

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

              А вот если мы имеем заранее неизвестный набор типов и понятия ни имеем — что с ними делать… зачем это? Куда это? Как это?


              1. 0xd34df00d
                29.11.2018 01:06

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


              1. Cerberuser
                29.11.2018 06:12

                В Rust для этого есть impl Trait и Box<dyn Trait>. Как раз для примера из коммента от 0xd34df00d, когда мы не знаем, что за сущность нам пришлют, но знаем, что она будет уметь некоторую нужную нам функциональность.


              1. Ogra
                29.11.2018 09:38

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

                Да, только на деле потом оказывается, что программист в качестве типа ответа прописал String, в базе оно лежит как VARCHAR(255), и для того, чтобы добавить в варианты картинку, придется переписать десять классов и три шаблона. А сделать это надо буквально вчера.


                1. mayorovp
                  29.11.2018 09:59

                  То есть лучше было бы начать переписывать не глядя на типы, и словить баги в 10 классах и 3х шаблонах? :-)


                  1. Druu
                    29.11.2018 10:04

                    Так может эти баги никто и не заметит, чего вы волнуетесь сразу! :)
                    В итоге силы — сэкономлены, заказчик — доволен. Пока… :)


                    1. mayorovp
                      29.11.2018 10:22

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

                      С тех пор прошло уже 2 года. До сих пор не работает, уже 4 года в сумме. Возможно, это и правда кому-то кажется нормальным…


                      1. Ogra
                        29.11.2018 11:05
                        -1

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


                        1. mayorovp
                          29.11.2018 11:16

                          Дело в подходе. Нормально ли оставлять недоделки на потом. Если нормально — то динамическая типизация и правда может помочь сделать что-то быстрее и уложиться в сроки (первые несколько раз).


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


                      1. KostaArnorsky
                        29.11.2018 20:35

                        А он вообще нужен? Что за сервис такой, который четыре года не работает, а никто не чешется?


                        1. mayorovp
                          29.11.2018 22:13

                          А фиг его знает. Его другой подрядчик делал…


                  1. Ogra
                    29.11.2018 10:09

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


                    1. Druu
                      29.11.2018 10:18
                      +1

                      А за счет какого волшебства у вас в динамическом ЯП количество классов уменьшится?


                      1. Ogra
                        29.11.2018 10:47
                        -1

                        Ну а как иначе? Вот был у нас ответ с типом String, а теперь должен быть и String, и Image. Начинаем переписывать!
                        Для начала введем базовый класс, AbstractAnswer, и от него сделаем двух наследников: AnswerString и AnswerImage. Потом добавим к этому фабрику AnswerFactory — она будет брать сериализованный JSON, пришедший от API, и порождать нужный класс. Конечно же, у каждого из классов будет свой метод render, каждому нужен свой шаблон…
                        В динамическом языке все будет попроще, менять будем только рендер:

                        if (typeof(answer) == "object" && answer.type == "image") {
                            return renderImage(answer.image);
                        } else {
                            return renderText(answer);
                        }
                        

                        Это только фронтэнд, про бэкэнд я молчу.

                        Речь на самом деле не про статическую/динамическую типизацию, а про номинальную/структурную. Просто с динамическим рантаймом структурная типизация дается проще.


                        1. Druu
                          29.11.2018 10:59

                          Ну а как иначе? Вот был у нас ответ с типом String, а теперь должен быть и String, и Image. Начинаем переписывать!

                          Ничего не начинаем, просто пишем в типе возврата string | image, а потом тот самый ваш код выше.


                          1. Ogra
                            29.11.2018 11:04

                            Для того, чтобы у вас можно было вернуть или string, или image, вам понадобится рантайм с динамической типизацией, или рефлексией. Это к стат.анализу flow или транспилеру TypeScript можно приписать string | image.


                            1. mayorovp
                              29.11.2018 11:14

                              Нет. Для того чтобы передавать string | image, совершенно необязательно иметь в рантайме полную информацию о типах. Достаточно чтобы разные типы были различимыми.


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


                              1. VolCh
                                30.11.2018 12:24

                                Рантайм динамического языка должен обеспечивать гарантии, даваемые системой типов языка. Если я написал function a(int b) на динамическом языке, то рантайм должен дать TypeError при попытке объект, например, передать.


                                1. mayorovp
                                  30.11.2018 12:39

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


                                  В том же С++, например, рантайм в общем случае ничего не знает про типы передаваемых параметров. Но это ничуть не мешает существованию std::variant или даже std::any


                                  1. VolCh
                                    30.11.2018 12:46

                                    А какое отношение эти типы имеют к, например, PHP? )


                                    1. mayorovp
                                      30.11.2018 12:49

                                      И правда, какое же? И при чем тут PHP?


                                      1. VolCh
                                        30.11.2018 12:59

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


                                        1. mayorovp
                                          30.11.2018 13:09

                                          И что? Как это подтверждает тезис что динамическая типизация нужна для типов-объединений?


                                          1. VolCh
                                            30.11.2018 17:15

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


                                            1. mayorovp
                                              30.11.2018 17:16

                                              Тогда зачем вы это пишите в этой ветке?


                                              1. VolCh
                                                30.11.2018 17:16

                                                И, кстати, у рантайма не может быть динамической типизации.


                                                1. mayorovp
                                                  30.11.2018 17:19

                                                  Хорошо, но при чем тут PHP?


                                                  1. VolCh
                                                    30.11.2018 20:53

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


                        1. mayorovp
                          29.11.2018 11:10

                          Во-первых, вы писали про "переписать 10 классов", а не про "написать 10 классов".


                          Во-вторых, не выдавайте свои фантазии на тему ООП за недостатки статической типизации. Нахрена тут фабрика, да еще и абстрактная?


                          В-третьих, исходно предлагалось, вообще-то, использование готовых языковых либо библиотечных механизмов вместо наследования. Вот я пишу: std::variant<std::string, Image> — где тут наследование и лишние классы?


        1. areht
          28.11.2018 21:53

          Так у вас на входе юзер, на выходе наличие .send() — вопросов нет.


        1. 0xd34df00d
          29.11.2018 01:05

          Вы вызываете эту функцию, получаете объект, а потом через него отправляете сообщение object.send(«hello»).

          И в чём вопрос?


          class HasSend a where
            send :: a -> String -> IO ()
          
          data SMS = ...
          instance HasSend SMS where
            send = ...
          
          data Email = ...
          instance HasSend Email where
            send = ...
          
          data Websocket = ...
          instance HasSend Websocket where
            send = ...
          
          data SomeHasSend where
            SomeHasSend :: HasSend a => a -> SomeHasSend
          
          someSendForUid :: UserId -> SomeHasSend
          someSendForUid uid | wantsSMS uid = SomeHasSend SMS
                             | wantsEmail uid = SomeHasSend Email
                             | wantsWS uid = SomeHasSend Websocket
          
          doStuff :: UserId -> IO ()
          doStuff uid = case someSendForUid uid of
            SomeHasSend hasSend = send hasSend "wassup"

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


        1. ApeCoder
          29.11.2018 19:01

          Допустим, вам надо отправить сообщение юзеру, для этого нужно получить канал отправки по id юзера, а потом отправить это сообщение по каналу. У вас есть функция, которая принимает на вход user_id, на выходе отдает один из нескольких типов данных: или экземпляр класса SMS, или экземпляр класса Email, или экземпляр класса Websocket, или вообще null.

          То есть вы думаете что результат является каналом отправки, и SMS, Email и Websocket являются каналами. Это похоже на описание типов


          1. Kroid
            30.11.2018 15:09

            Ну как бы это разные каналы. Если мы объединяем в один тип SMS, Email и Websocket, то с тем же успехом можно объединить в один тип строку, число и булево значение — всё это данные. Только зачем тогда вообще типы нужны, если мы их все приводим к одному супертипу?

            UPDATE Мне в этом смысле golang нравится — есть структуры (типы), а есть интерфейсы (поведение). В итоге разные структуры (SMS, Email и Websocket) реализовывают одно и то же поведение — отправку сообщения.


            1. KostaArnorsky
              30.11.2018 19:02

              Вы не объединяете в один тип, вы описываете интерфейс канала и получаете разные имплементации. Но у вас не падает все внезапно, когда channel.send({ duck: «Я маленькая уточка и знать не знаю про никакие контракты.» });


            1. ApeCoder
              30.11.2018 19:27

              > один тип строку, число и булево значение — всё это данные.

              Все это объекты. Да, есть типы есть подтипы.
              > олько зачем тогда вообще типы нужны, если мы их все приводим к одному супертипу?

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


    1. VolCh
      30.11.2018 12:14

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


  1. mayorovp
    28.11.2018 14:31

    При попытке передать туда массив объектов другого типа компилятор будет ругаться.

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


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

    Не является особенностью динамической типизации.


    Я как-то сталкивался с C# и искренне не мог понять, почему он не дает мне сложить число со строкой.

    Вранье, C# это разрешает делать.


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

    В том же C# любое значение может быть приведено к object и положено в object[].


    Автоматическое приведение к числу. Это одна из моих любимых вещей.

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


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


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

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


    1. paratagas
      28.11.2018 14:56

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

      В том же C# любое значение может быть приведено к object и положено в object[].
      В JS для этого не нужно создавать объект. Можно положить в массив как примитивы, так и объекты:
      const customArr = [1, 'two', {three: 'three'}];


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

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

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


      1. mayorovp
        28.11.2018 15:05

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

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


        В JS для этого не нужно создавать объект.

        Почему приведение типа к object вы называете "созданием" объекта?


        1. paratagas
          28.11.2018 15:20

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

          Почему приведение типа к object вы называете «созданием» объекта?
          Насколько, я знаю, в C# массивы не могут иметь разные типы данных. Это принцип проектирования массивов в этом языке. Поэтому я предполагаю, что вы имели ввиду создание объектной оболочки над примитивом. В отличие от моего примера, где используется сам примитив.


          1. mayorovp
            28.11.2018 15:26

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

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


            Насколько, я знаю, в C# массивы не могут иметь разные типы данных. Это принцип проектирования массивов в этом языке. Поэтому я предполагаю, что вы имели ввиду создание объектной оболочки над примитивом. В отличие от моего примера, где используется сам примитив.

            Вас что, вручную заставляют создавать эту самую объектную оболочку? Так нет же, компилятор автоматически приводит любое значение к object! Что же до эффективности такой операции — не js-нику жаловаться на производительность упакованных объектов в c#...


            1. paratagas
              28.11.2018 15:52

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


              1. mayorovp
                28.11.2018 15:55

                … и все равно у этого подстраивания есть какие-то пределы.


      1. kagetoki
        28.11.2018 16:03

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

        придется обертывать в try/catch практически весь код, а не предположительно наиболее уязвимые участки кода

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

        Точность, достойная тру-разработчика. Просто восхитительно. Надеюсь, вас в жизни не допустят до написания кода систем, имеющих хоть какое-то отношение к человеческому здоровью/жизни.


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


        1. paratagas
          28.11.2018 16:30

          Точность, достойная тру-разработчика. Просто восхитительно. Надеюсь, вас в жизни не допустят до написания кода систем, имеющих хоть какое-то отношение к человеческому здоровью/жизни.
          Спасибо за характеристику меня, как разработчика)) Но я в своем развернутом мнении выше как раз и описал, что это
          … неприемлемо в ПО, предназначенном, скажем, для медицины и космоса, но вполне допустимо для показа на карте 10 ближайших баров в округе
          Особенно, если приложение для показа баров должно было быть готово «вчера», но узнали об этом, как обычно, сегодня.


        1. int03e
          28.11.2018 16:37

          Софт для самолетов никто на JS не пишет. Для разных типов задач существуют разные подходы и инструменты, это понятно. Писать софт для марсохода — это одно дело, нафигачить MVP за месяц для заказчика (а иначе он уйдет) — это совсем другое.


  1. NN1
    28.11.2018 14:43

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

    TypeScript:

    // Динамика это круто
    function f(a, b) {
    	// Будь что будет
    	return a * b;
    }
    
    f(1, "abc") // Ну и что, что получаем NaN, зато не надо писать типы. Красота ! 
    
    
    // Укажу динамический тип. Динамика это круто
    function g(a: any, b: any): any {
    	// Будь что будет
    	return a * b;
    }
    
    // Статика это тоже круто
    function h(a: number, b: number): number {
    	// Умножаем числа
    	return a * b;
    }
    
    h(1, "abc") // Плохой компилятор, не хочу ошибку компиляции


    C#:
    public class Program
    {
    	// Динамика это круто
    	public static dynamic g(dynamic a, dynamic b) {
    		// Будь что будет
    		return a * b;
    	}
    	
    	// Статика это тоже круто
    	public static int h(int a, int b) {
    		// Умножаем числа
    		return a * b;
    	}
    	
        public static void Main()
        {
    		g(1, "abc"); // Кидает исключение, ерунда. Зато принимает любые типы
    		h(1, "abc") // Плохой компилятор, не хочу ошибку компиляции  
        }
    }


    1. ApeCoder
      28.11.2018 16:44

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


  1. eugenk
    28.11.2018 14:48
    +1

    Небольшое лирическое отступление. Прочитал комменты и в очередной раз убедился что прогеры народ всё-таки (как бы это помягче сказать...) совершенно особый. Как то с трудом могу себе представить подобный спор об инструментах (а ведь говорим мы именно о них !) в среде например сантехников. Ещё большое спасибо любимому хабру, что тут всё-таки атмосфера научного диспута, а не бардака при пивном ларьке! А то в начале 2000-х на соответствующих конфах помню такое…
    И да. Очень хотелось бы чтобы подобную статью написал кто-то из сильных js разработчиков со своей колокольни. Надеюсь эта дискуссия кого-нибудь из них сподвигнет. Буду каждый день просматривать хаб по джаваскрипту :))


    1. musuk
      28.11.2018 16:33
      +1

      >среде например сантехников
      Да ладно.
      Метапол против полипропилена.
      А если наберёте Festool, то откроете ворота в ад.


      1. sshikov
        28.11.2018 21:33

        Именно. Совсем недавно на youtube наблюдал, как «спорили» два ремонтника (ну, не совсем спорили, и не совсем сантехники, а скорее сборщики мебели).

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


      1. Ogra
        29.11.2018 09:42

        Метапол против полипропилена.

        Да проще. Лён против фума =)


        1. Free_ze
          29.11.2018 12:00

          Актуально уже скорее фум против унилока)


          1. Ogra
            29.11.2018 12:04

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


            1. Free_ze
              29.11.2018 12:12

              Кошмар сантехника-хипстера: пластиковые трубы, лён и сверху красочкой…


    1. wert_lex
      28.11.2018 20:28
      +2

      Давайте я попробую. У меня есть интересный опыт вида Scala -> JavaScript (и сервер и фронт) -> Typescript -> Scala.


      1. всем кто пишет на строготипизированных языках нужно обязательно попробовать языки со слабой типизацией и наоборот. Например, на домашних поделках внезапно оказывается, что можно не писать тонны бойлерплейта (привет Java), и не воевать с компилятором. Такое вот чистое творчество.
      2. как правильно заметили выше — в слаботипизированных языках типы никуда не исчезают. Они просто становятся сильно менее строгими и позволяют больше фривольностей.
      3. судя по всему существует определённый предел для проекта с со слабой типизацией. Это и количество задействованных одновременно людей (интерфейсов нет), и объем проекта (типы — документация). Выше некоторого объема начинается настоящая боль.
      4. насколько я вижу сейчас, в мейнстримовых языках типы — не панацея, но здорово помогает. Системы типов либо не достаточно мощные (Java, например), чтобы выражать всё задуманное. Либо чересчур сложные (Haskell, Agda, Idris), чтобы на этом можно было писать бизнес-код средними бизнес-программистами. Плюс частенько порождает некоторые теоретические тупики, которые императивно решаются в десяток строк кода, а функционально заставляют весь мир намотать на себя. Есть Scala, которая пытается быть и тем и другим, но у неё свои проблемы есть.
      5. как минимум в мейнстримовых языках со строгой типизацией от написания тестов никуда не сбежать — система типов не достаточно мощная, чтобы выразить все операции с данными, плюс в игру вступают сайд-эффекты, внешний мир, базы данных, итд. Со стороны приёмочного тестирования, едва ли тестов меньше. В юнит-тестах — возможно.

      Вообще, безотносительно самого JS мне очень нравится связка JS + TS из-за gradual typing. Хочется писать быстро — пишешь быстро на JS и надеешься на лучшее. Хочется писать надежнее — развешиваешь типы и спишь по ночам крепче.


      Если кому-то интересно, я с радостью могу поотвечать на волнующие вопросы :)


      1. PsyHaSTe
        28.11.2018 21:20

        Немного не в тему, но как вообще со скалой, с вакансиями и т.п.? Мне многие знакомые советуют с C# свичнутся, для большей продуктивности, но я не вижу ни вакансий, ни потребностей рынка… С удовольствием почитал бы статью, но сойдет и комментарий.


        1. wert_lex
          28.11.2018 21:34
          +1

          Они есть, но если сравнивать с C#, Java и тем более JS — значительно меньше.
          Вообще на мой взгляд Scala сейчас переживает не лучшие времена. Волна хайпа прошла, scala-only фичи потихоньку подвозят в мейнстримовые языке (те же лямбды в Java заставили задуматься очень многих о том действительно ли им всё это надо или и джавы хватит). Kotlin, опять же появился.


          Вообще по своему Scala-опыту могу сказать, что использовать Scala как Better Java не очень хорошо выйдет. Оно хоть и достаточно интероперабельно, но не так всё гладко. Плюс многие Scala-библиотеки делают вещи scala-way (чем и хороши). В этом плане Kotlin выглядит сильно лучше.


          Но и нельзя сказать что рынок совсем глух. Даже на неактивный в данный момент профиль на upwork раз в пару недель уж точно инвайт на Scala-интервью прилетает.


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


          1. PsyHaSTe
            28.11.2018 22:04

            Просто хочется нормальный паттерн-матчинг, нормальные атд (и гадт при должной фантазии), типы высших порядков, literal типы, path-dependent типы и т.п., человеческие трейты, импликиты, макросы, развитая и популярная ФП экосистема… Беттер джава не сильно инетерсует, скорее интересует вариация "раста без борроу чекера, на гц, но с хкт". Просто у меня выбор как раз между ними. С одной стороны не очень популярная скала. С другой — раст, которым не очень легко заниматься в мейнстрим разработке, даже коллеги-растовчане на нетривиальные вопросы предпочитают отвечать в духе "а нафига ты пишешь это на расте?". Хаскель сильно академичный + мне не нравятся многие вещи (вроде специального синтаксиса для filter, странные операторы вроде ++ и т.п.). А больше и нет альтернатив, где можно писать типичные-задачи более удобно и красиво.


            1. Cidevant_Von_Goethe
              28.11.2018 22:17

              A что плохого в функции filter в Haskell?

              filter (>5) [1,2,3,4,5,6,7,8]
              

              IMHO, это само совершенство!


              1. PsyHaSTe
                28.11.2018 22:23

                Я про [x | x <- [50..100], x 'mod' 7 == 3]. Как по мне, странноватое решение.


                1. Cidevant_Von_Goethe
                  28.11.2018 23:14

                  это не filter а list comprehension


                  1. PsyHaSTe
                    28.11.2018 23:19

                    Ну вот часть x 'mod' 7 == 3 это чисто filter, зачем её выносить в синтаксис если можно было бы просто вызвать как функцию как в вашем примере не сильно понятно.


                    1. Cidevant_Von_Goethe
                      28.11.2018 23:30

                      В самом описании list comprehension в Haskell написано, что это «syntax sugar».

                      filter (>5) [x | x <- [1..]]
                      тоже самое что и
                      [x | x <- [1..], x > 5]


                    1. 0xd34df00d
                      29.11.2018 01:19

                      Это вы ещё SQL-like list comprehensions не пробовали. Ну и следующий раздел про monad comprehensions заодно.


            1. 0xd34df00d
              29.11.2018 01:21

              Да не такой уж хаскель академичный. Академичность — это какой-нибудь Idris или там, не знаю, Zombie.


              А ++ мне тоже не нравится. Но есть же <>, как и для всех остальных моноидов.


              1. Cerberuser
                29.11.2018 06:15

                Я ж надеюсь, Вы не про этот Zombie?..


                1. 0xd34df00d
                  29.11.2018 08:03

                  Нет, про этот.


              1. PsyHaSTe
                29.11.2018 15:09

                Idris это вообще уже удел оторванных чистых математиков, которые еще и ХМ без ограничений юзают :)


                Хотя я, конечно же, могу ошибаться. Для меня это 'Known fact', без какого-то конкретного достоверного источника.


                1. 0xd34df00d
                  29.11.2018 15:47

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


                  1. Druu
                    29.11.2018 15:58

                    Гы, а математики считают

                    Кек. Ну математики пусть считают, они умеют иногда :)


        1. sshikov
          28.11.2018 21:35

          Со скалой вас без вопросов возьмут в мир BigData (Spark и т.п.). Это та еще потребность рынка.


          1. PsyHaSTe
            28.11.2018 22:01

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


            1. sshikov
              28.11.2018 22:03

              Для начала, включите в поиск Hadoop и Spark, и оцените разницу в числе вакансий. У нас вот де факто Scala == Java, на чем хочешь, на том и пишешь.

              Хороший оффер это сколько?


              1. PsyHaSTe
                28.11.2018 22:06

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


                Если по деньгам, мне кажеся от 3к белыми на сениор позицию это хорошие деньги.


                1. sshikov
                  29.11.2018 20:46

                  Это в рублях в пределах 180к? Так это совсем не фокус для сеньорских вакансий. Но только реально сеньорских, конечно…


                  1. PsyHaSTe
                    29.11.2018 22:51

                    Это 198 600,30 по текущему курсу, как подсказывает гугл.

                    Это моё личное мнение. Знаю людей, которые больше 250 получают и им мало, но я пока не дорос. Но стремлюсь достаточно активно.


          1. creker
            29.11.2018 01:28

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


            1. sshikov
              29.11.2018 20:36

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

              А то что можно писать скажем на питоне или на R — это понятно.


      1. eugenk
        29.11.2018 07:12

        Спасибо, действительно интересный коммент. Извините, немного не в тему, а с scala.js Вы работали? По-моему вообще самое лучшее что сейчас существует для фронтэнда. К сожалению вынужден был от этого отказаться из-за совершенно безобразной поддержки в IDE. Сейчас работаю на ts + php в рамках единого проекта под vs code. Но если бы была нормальная поддержка, с огромным удовольствием ушел бы с ts на scala.
        P.S. Да, на всякий случай ко всем. Если хотите изучить функциональщину, играться со scala будет далеко не лучшей идеей. Слишком многое scala позволяет и Вы ничему не научитесь. Начните с Haskell. Он просто не позволит Вам писать не идеоматично. Сам сейчас именно его потихоньку изучаю.


        1. wert_lex
          29.11.2018 15:52

          Со scala.js не работал сам, но на одном из проектов работал с фронтендерами, которые как раз выкидывали всё написанное ими же на scala.js в пользу более джаваскриптовых фреймворков.
          Тогда меня фронтенд не интересовал совсем, но эти ребята говорили, что:
          а) реальной гетерогенности нет
          б) тулинг ужасный
          в) готовые фронтендовые либы очень грустно интегрируются
          г) и если всё правильно помню, то на выходе у них получался огромный бандл


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


        1. sshikov
          29.11.2018 20:40

          У нас в отделе был такой опыт. Очень любили scala.js, на мой взгляд — незаслуженно. Результаты того не стоят.

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


  1. FRiMN
    28.11.2018 14:50

    Мне кажется, что автор неоправданно сравнивает C# с JS, противопоставляя статическую типизацию динамической. JS слабо типизирован, но не все языки с динамической типизацией слабо типизированы. Далеко не во всех языках с динамической типизацией можно к строке прибавить число.


    Кроме того, не с проста во многих языках со статической типизацией есть тип any. Даже в C# есть утиная типизация.


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


    Как бы вы ответили на вопрос: какой язык лучше, со слабой статической типизацией, или сильной динамической?


    1. kagetoki
      29.11.2018 09:45

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

      Язык либо динамически типизирован, либо статически. Он не может "плавать" где-то посередине. В языке могут быть различные средства для того, чтобы что-то почерпнуть из другого мира, например dynamic в C#. Но от того, что в сишарпе есть dynamic он не стал динамически типизированным.
      Еще, например, в С# есть неявное приведение типов. Но оно работает, только если заранее описать, как оно должно работать, оно не работает для всего и всегда. Да и делается обычно это для каких-то совсем очевидных случаев, вроде int -> long.


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


      Как бы вы ответили на вопрос: какой язык лучше, со слабой статической типизацией, или сильной динамической?

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


      1. Ogra
        29.11.2018 09:55

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

        Язык: Си, зачем: fast inverse square root =)


        1. 0xd34df00d
          29.11.2018 15:49

          Его никто не мешает сделать в языке со строгой статической типизацией, если там есть, например, coerce. Заодно компилятор проверит, что типы действительно имеют одинаковое представление.


      1. KvanTTT
        29.11.2018 12:27

        Да и делается обычно это для каких-то совсем очевидных случаев, вроде int -> long.

        Корректней сказать для примитивных типов и при отсутствии потери информации int (4 байта) -> long (8 байт). Обратное преобразование породит ошибку компиляции.


      1. FRiMN
        30.11.2018 12:13

        … от того, что в сишарпе есть dynamic он не стал динамически типизированным

        Возможно, я что-то упускаю, т.к. не знаком с C#, но разве dynamic не использует DLR? Это же не библиотека, а часть языка.


        Язык либо динамически типизирован, либо статически.

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


        По поводу статьи:


        Статическая типизация не панацея. Например, статическая типизация в Java не помогает отловить NullPointerException.
        Вот, чуваки утверждают:


        A statically typed language can catch some errors in your program before it even runs, by analyzing the types of your variables and the ways they're being used. A dynamically language can't necessarily do this, but generally you'll be writing unit tests for your code either way (since type errors are a small fraction of all the things that might go wrong in a program); as a result, programmers in dynamic languages rely on their test suites to catch these and all other errors, rather than using a dedicated type-checking compiler.

        И действительно, как часто, по вашему, у программистов на языках с сильной динамической типизацией возникает несогласование типов (none/null и т.п. не в счёт)? А в статически типизированных?
        И каким образом компилятор статически типизированного языка поможет мне защититься от внешних данных с неверным типом?


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


        Благодаря определению типов во время исполнения программы сильно облегчается метапрограммирование. Очень сильно облегчается. Оно, в свою очередь, упрощает всю остальную работу.

        Благодаря гибкости кода в рантайме (см. тот же duck typing) и интроспекции (анализ свойств объектов и кода) получается на порядок проще и быстрее писать универсальные алгоритмы и конструкции вроде декораторов, всяческих ORM и подобных вещей. Это сильно упрощает интерфейсы библиотек, что в совокупности ведёт к более простому коду и к плавной кривой обучения новичков.

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

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

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

        И ещё от туда же:


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

        Ну и где мои коллеги на С# пишут всякие dependency injection, visitor, перегружают методы, наворачивают какие-то абстракции, вводят интерфейсы, я со своим питоном просто проверяю, есть ли свойство через hasattr(), или узнаю тип через type(). Так что, сила питона в интроспекции и рефлексии.


        1. PsyHaSTe
          30.11.2018 12:38
          +1

          Статическая типизация не панацея. Например, статическая типизация в Java не помогает отловить NullPointerException.

          Потому что в жабе достаточно слабая типизация. В любом современном языке у вас есть Maybe T/Option<T>/..., где вы явно говорите, что тут может быть нулл, и вы не забудете его проверить, и не будете проверять там, где не надо. Так что компилятор именно что помогает отловить нулл. Более того, в C# 8.0 это ключевая фича новой версии языка.


          since type errors are a small fraction of all the things that might go wrong in a program

          Практически любую ошибку можно выразить на уровне типов. Это не всегда полезно, и можно упороться тем, чтобы сделать классы IntOne, IntTwo и т.п., описывая все их свойства (такие завтипы для бедных), но в моей практике скорее все наоборот: типы позволяют найти почти все ошибки, кроме совсем дурацких, вроде написали 1 вместо 10. Которые легко чинятся, потому что совершенно попиюще кричат из кода "Я НЕВЕРНОЕ ЗНАЧЕНИЕ" :)


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


          Посмотрите на ORM в каких-нибудь шарпах. Как по мне, во-первых сильно проще их писать (потому что ОРМ сама по себе сложная штука, а все сложные штуки, как я уже выше писал, проще писать с поддержкой от компилятора), во-вторых их проще использовать, потому что компилятор помогает понять, что можно делатЬ, что нет, и т.п.


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


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

          Интерфейсы и экзистенциальные типы в помощь.
          Зато в динамике легко забыть, какой именно набор полей может быть у сущности, и начать передавать еще один, но не во всех местах, а потом радоваться взлетающим undefined is not a function.


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

          Надо имплементировать только тот интерфейс, которым пользуется вызывающий код. То есть тот, которым вы и так неявно пользуетесь, когда надеетесь, что у объекта есть метод .foo(). Только в другом случае вы не надеетесь, а точно знаете, что он будет.


          Ну и где мои коллеги на С# пишут всякие dependency injection, visitor, перегружают методы, наворачивают какие-то абстракции, вводят интерфейсы, я со своим питоном просто проверяю, есть ли свойство через hasattr(), или узнаю тип через type(). Так что, сила питона в интроспекции и рефлексии.

          В C# тоже так можно, только так не делают. И не потому, что медленно, а потому что ненадежно. Не говоря про то, что визитор ортогонален hasattr'у.


        1. Free_ze
          30.11.2018 12:41

          Статическая типизация не панацея.

          Никто не говорит, что это панацея. Но целый пласт ошибок убирает.

          как часто, по вашему, у программистов на языках с сильной динамической типизацией возникает несогласование типов (none/null и т.п. не в счёт)?

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

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


          1. PsyHaSTe
            30.11.2018 12:50

            Да люди не знают. что в нормальном проекте даже 500 файлов перелопатить (например, у меня такое было с миграции с монго драйвера версии 1.0 на 2.0) не вызывает проблем в статическом языке, а в динамике обычно дернуться боишьсЯ, чтобы где-то не взорвалось, файлики меняются аккуратно, по одному, и желательно с фоллбеком. Поменять треть проекта так, чтобы сохранить работоспособность, это для людей совершенно непонятно :)


            1. Free_ze
              30.11.2018 13:29
              +1

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


            1. sentyaev
              30.11.2018 15:18

              а в динамике обычно дернуться боишься

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

              Остальное только тесты. Я и на Python и на C# и на JS пишу, сильно не страдают от динамической типизации, хотя я и согласен с тем, что статическая типизация дает немного разгрузить мозг, ибо не нужно весь стек вызовов вместе с тем как по этому стеку данные трансформируются в голове держать постоянно.

              Но, хорошее тестовое покрытие считаю более важным, чем наличие/отсутствие типизации.


              1. Free_ze
                30.11.2018 15:51
                +1

                В этом случае статическая типизация вам только ошибки компиляции покажет

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

                Это и погрепать можно

                Нельзя. Греп не видит контекста. Да и вы это в 2к18 руками будете делать?!

                Но, хорошее тестовое покрытие считаю более важным, чем наличие/отсутствие типизации.

                Более/не более — это софизм. Зачем теплое с мягким сравнивать? Если будет и то, и другое — это только лучше.


                1. sentyaev
                  30.11.2018 16:35

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

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

                  Сильное утверждение. PyCharm кстати очень помогает, когда можно использую его, когда нет — grep. Соседи вообще в vim'e сидят и все у них получается.
                  Более/не более — это софизм. Если будет и то, и другое — это только лучше.

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


                  1. Free_ze
                    30.11.2018 17:17

                    а одних тестов — достаточно.

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

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

                    Статическая типизация — это тесты, которые уже написаны за вас и запускаются при каждом изменении. Это взаимодополняющая штука с юнит-тестами.


                    1. sentyaev
                      30.11.2018 19:18

                      Ага, есть время покрывать тестами, но нет писать типы?)

                      Я такого не говорил.
                      Это взаимодополняющая штука с юнит-тестами.

                      И с этим я не спорю, а согласен на все 100%


                    1. VolCh
                      30.11.2018 20:55

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


          1. lega
            30.11.2018 14:14
            -1

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


            1. mayorovp
              30.11.2018 15:29

              От того, что вы повторите эту цитату еще несколько раз, правдой она не станет.


  1. kozar
    28.11.2018 15:08
    +1

    Из последнего абзаца получается интересный вывод:

    Если только начинаете изучать разработку, начинайте со статической типизации

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


  1. Whiteha
    28.11.2018 15:11

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


  1. botyaslonim
    28.11.2018 15:13
    +1

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


    1. PsyHaSTe
      28.11.2018 19:04

      Зависит от того, как эти типы использовать.


      Если создать они огромный byte[] Memory и дальше с ним по указателям работать, то толку будет мало. А можно сделать тип IsOddNumberLargerThanTen и во время компиляции получить ошибку, при попытке проверить, является ли число простым.


  1. Strain
    28.11.2018 15:18
    +1

    Отличная тема для знатного вброса для холиваров. Мои поздравления.


  1. random
    28.11.2018 15:58
    +1

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

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

    «Компилятор меня проверил, я все сделал правильно», «IDE… проверяет на ошибки». «JS-ник, берет этот мой тип… и начинает использовать его неправильно»

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

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


    1. eugenk
      29.11.2018 06:47

      Честно говоря про страх подумал только после Ваших слов. Да, и это имеет место быть. Хотя я бы называл это немного по-другому — защита от совсем уж глупых ошибок. Но для меня лично главное пожалуй это хорошая поддержка IDE. Автокомплит, показ структуры и т.п. Возможно крутые перцы тут меня засмеют, мол православный и духоскрепный прогер обязан уметь писать код хоть в notepad-е, но моя практика показывает, что писать код для современных окружений (ОС, броузер и т.п.) можно только с хорошей поддержкой IDE. Иначе слишком много деталей приходится держать в голове и процесс превращается в мучение. Для динамических же языков поддержка IDE увы, но просто не может быть хорошей.


      1. gatoazul
        29.11.2018 14:51

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

        Требуются абстракции следующего уровня, и в окружении, и в языках.

        Использование IDE несколько тормозит этот процесс, но он неизбежен.


    1. PsyHaSTe
      29.11.2018 15:16

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

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


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

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


      1. random
        29.11.2018 20:15
        -2

        Нежелание — не эмоция. Это реакция на эмоцию.


        1. PsyHaSTe
          29.11.2018 20:22

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


          1. random
            30.11.2018 00:25
            -1

            Человек, в страхе совершить ошибку, ошибается еще больше?

            Строго говоря да. Человек в страхе более подвержен ошибкам мышления.

            Читаю в сердцах, что тема вам не безразлична. Если так, рекомендую книгу «Укрощение амигдалы»


    1. ApeCoder
      29.11.2018 22:42
      +2

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

      Этот демагогический прием называется "чтение в сердцах". Если вы откроете поисковик и напишете "why static typing" вы легко найдете другие доводы за. И почему это может быть не эмоция, а расчет?


      «Компилятор меня проверил, я все сделал правильно»

      А так же "IDE мне подсказывает" — уже не только про ошибки.


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

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


      Когда вы говорите "не спасает от ошибок" вы имеете ввиду не спасает от всех ошибок или нет вообще никаких ошибок, от которых он спасает?


      И в дело вступает любимая психологическая защита

      И опять "чтение в сердцах"


      1. PsyHaSTe
        29.11.2018 22:53
        +1

        Почитал ваш ответ, понял, что зря вообще начинал отвечать данному товарищу.


      1. random
        30.11.2018 00:48
        -3

        Если вы откроете поисковик и напишете «why static typing» вы легко найдете другие доводы за.
        Именно так работает когнитивное искажение предвзятость подтверждения.

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


        1. PsyHaSTe
          30.11.2018 01:19

          Давайте повторим

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

          Простите, но это не похоже на ответ на поставленный вопрос, а скорее псевдофилософское словоблудие.


          1. random
            30.11.2018 02:09

            Давайте повторим. Вот утверждения:

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

            Привести примеры взаимодействия?
            Отвалившееся соединение с базой данных. Ответ от API с некорректными данными в неожиданном формате. Пользователь сделал что-то не то.

            Надеюсь, так понятней.

            Если вопрос был в том, спасает ли компилятор вообще от каких-то ошибок, то… Риторические вопросы ж не требуют ответа?


            1. Druu
              30.11.2018 04:36

              Если вопрос был в том, спасает ли компилятор вообще от каких-то ошибок, то… Риторические вопросы ж не требуют ответа?

              А вопрос был не риторическим и ответа требовал.


        1. ApeCoder
          30.11.2018 06:41
          +2

          Именно так работает когнитивное искажение предвзятость подтверждения.

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


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

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


    1. sentyaev
      30.11.2018 15:24
      +2

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

      Это не страх, это Уверенность. Когда у вас статическая типизация вы просто уверены, что все стыкуется. Плюс получаем поддержку IDE для рефакторинга.

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

      А он и не должен, это делают тесты.


      1. IvanNochnoy
        30.11.2018 15:38

        Проблема с тестами, в том, что они не проверяют правильность выполнения программы, а лишь то, что программа соответствует некоторой спецификации, которую программист считает правильной. Но то, что сама спецификация может быть ошибочна — никак не проверяют. Проще говоря — тесты могут найти ошибку в коде, но не ошибку в голове. Посмотрите на любой проект на GitHub со 100% покрытием тестами, а затем посмотрите на количество issue в этих проектах. Вопрос: откуда берутся баги при 100% покрытии? В то же время, Type Driven Development очень часто находит ошибки именно в логике работы программы. Так что лично мне ясно, что, хотя тесты и нужны, но они никак не могут заменить хорошей типизации.


        1. sentyaev
          30.11.2018 15:57

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

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

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

          С другой стороны, я считаю, что по важности, статическая типизация это 10%, а тесты это 90%.

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

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


          1. IvanNochnoy
            30.11.2018 17:15

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


            Пример 1
            Допустим у Вас есть объекты Person, Security и GentlemanClub. Клуб мужской и туда запрещен доступ женщинам, для этого есть объект Security, который проверяет свойство Sex объекта Person, и пропускает в клуб только если Sex == Male. Вы пишите тесты и все зашибись. Но внезапно Вы обнаруживаете, что в клубе есть женщины! Откуда? Дело в том что свойство Sex объекта Person изменяемое, и кто-то из клиентов изменил это свойство уже после того, как прошел проверку Security! Ваши тесты были зелеными, но, увы!, вы не подумали о том, что свойство может быть изменено, до тех пор, пока не получили багрепорт. А вот если бы, вместо того, чтобы писать тесты, которые мышей не ловят, вы бы сделали так, что свойство Sex устанавливалось только в конструкторе класса, то такая ситуация была бы невозможна в принципе.

            Пример 2
            Военные летчики чуть не убили себя во время вылета. Их истребители внезапно перестали подчинятся штурвалу и перевернулись вверх шасси. Все тесты были норм. Но дело в том, что при пересечении экватора значение широты стало отрицательным, и это привело к проблемам в каком-то приборе. А вот если бы программист вместо типа int использовал uint (беззнаковое целое), то компилятор не отстал бы от него, пока он не взял бы модуль от значения перед тем, как передать его в метод. Да, и, конечно, программеру не пришло в голову, что есть два полушария на земле.

            Пример 3
            Вы разрабатывете калькулятор индекса массы тела, есть метод Calculate(int height, int age, int weight). Вы считаете себя довольно стройным, но вызов функции с вашими данными приводит к тому, что у вас ожирение 3-й степени. Как так? Оказывается, данные нужно было указывать в фунтах и дюймах, а вы указали в метрах и килограммах. А вот если бы вы воспользовались такой штукой, как Units of measure, которая присутствует в F# то такой ошибки не возникло бы, так как без явного указания единиц измерения (70<inch>, 20<year>, 25<pound>) программа просто не откомпилировалась бы.

            Грубо говоря, компилятор говорит о синтаксически корректной программе, а тесты о логически корректной программе

            Глубокое заблуждение, как видите, типы бывают умнее программиста и его тестов.


            1. sentyaev
              30.11.2018 18:01

              Первый пример надуманный, в динамически типизированных языках тоже есть immutability, например в Elixir. Да и в том же JS можно сделать приватные переменные через замыкание. Это больше относится к проектированию, чем к типизации.

              Второй пример тоже надуман. Широта и долгота может быть представлена как отрицательное число. Если еще добавить высоту, то можно летать ниже уровня моря.
              Из вики Форматы_записи_географических_координат:

              Координаты (широта от ?90° до +90°, долгота от ?180° до +180°) могут записываться:

              в ° градусах в виде десятичной дроби (современный вариант)
              в ° градусах и ? минутах с десятичной дробью
              в ° градусах, ? минутах и ? секундах с десятичной дробью (исторически сложившаяся форма записи)
              Разделителем десятичной дроби может служить точка или запятая. Положительные знаки координат представляются (в большинстве случаев опускаемым) знаком «+» либо буквами: «N» — северная широта и «E» — восточная долгота. Отрицательные знаки координат представляются либо знаком «?», либо буквами: «S» — южная широта и «W» — западная долгота. Буквы могут стоять как впереди, так и сзади.

              Единых правил записи координат не существует.


              Третий пример хорош. Мне очень нравится эта фича F#. К сожалению в мэинстрим языках этого пока не наблюдаю.

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


            1. VolCh
              30.11.2018 20:41
              -2

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


  1. ilja903
    28.11.2018 16:03

    Я перешел от статики к динамике, наиболее полный ответ почему:
    www.infoq.com/presentations/Simple-Made-Easy
    www.youtube.com/watch?v=rI8tNMsozo0
    gist.github.com/oakes/c82cd08821ce444be6bf


  1. Deranged
    28.11.2018 16:05

    Да, я тоже в первый раз, когда попробовал написать что-то на QML малость прифигел.
    До сих пор интересует вопрос, какому гению пришла в голову идея в качестве базы для QML выбрать JS? И это во фреймворке на C++, где типизацией пронизано буквально всё, а в QML её особо нет, даже если захочется. Порог вхождения во фреймворк по этому получился просто астрономическим. Нужно не только уметь мыслить «по ООП-шному», но еще и по JS-ному. При чем и то и другое нужно уметь делать одинаково хорошо…


  1. SergeyKhorn
    28.11.2018 16:53

    Вообще, дело же в первую очередь в мышлении, в привычках. Совершенно не вижу причин для негодования или пренебрежительного отношения к тем, кто пишет на динамически типизированных языках.
    Огромная часть frontend'а — это landing pages и небольшие сайты, например, портфолио или промо. Можно, конечно, спорить frontend-программист ли вообще человек, встраивающий slider-widget на сайт, но так называются вакансии и позиции, на которых они работают. Годами. Суть же в том, что для выполнения этой работы, которой много, типизация просто-напросто избыточна, читай не окупает себя.
    Как только задача перестает быть тривиальной, она начинает требовать декомпозиции. И как следствие для ее решения выстраивается архитектура и подбираются паттерны. Да, трудно спорить о том, что ООП может поспособствовать развитию системного мышления, это то, что Вы описывали: системные объекты и их взаимодействия, но это вовсе не обязательно. Более того, не всегда опыт и владение языком со статической типизацией ведут к тому, что у человека появляются способности к системному мышлению и проектированию. Ровно как и то, что человек пишет, скажем, на JavaScript, не лишает его этих способностей.
    Кстати, качественное тестирование помогает решить не проблему TypeError, а logical errors.
    В любом случае, судя по Вами же описанному случаю, у Вас проблема заключалась не в разных языковых предпочтениях, а в том, что Ваш друг и коллега отказывался от этапа проектирования. Совсем ли он отказывался или настаивал на поэтапном проектировании, призывая выбрать agile-подход, не понятно, а может быть он вообще хотел сначала сделать прототип (как частенько бывает во frontend), но суть вашей проблемы именно в этом.
    А к написанию этой статьи Вас подвигло то, что вы распространили отдельно взятый частный случай, когда, пишущий на динамически типизированном языке, отказался от проектирования, на всех программистов использующих такие языки.
    Не все такие.

    staticTypes !== projectDisign && dynamicTypes !== noProjectDisign
    

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


  1. misha_shar53
    29.11.2018 15:52

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

    Но это не все типизация в языке может вообще отсутствовать.

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

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


  1. misha_shar53
    29.11.2018 16:06

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

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


  1. misha_shar53
    29.11.2018 16:16

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

    А еще лучше вообще забудьте о типизации как о дурном сне.


  1. LexLiven
    30.11.2018 02:42

    Есть подозрение, что вы не только из-за типизации не поняли друг друга. Есть два метода разработки — нисходящий и восходящий. Вы — приверженец восходящего подхода. Это когда сначала пишутся все функции, методы и прочие взаимодействия составляющих «кирпичиков», а потом из них собирается проект. А ваш друг — приверженец нисходящего. То есть ставится задача, которая постепенно дробится на все более мелкие части. То есть пишем функцию «MakeBest()», в ней пишем вызов «MakeGood()», и только потом создаем эту самую MakeGood(), прописывая что именно она должна сделать.

    Ваш подход позволяет реализовать более продуманную в техническом плане задачу, но требует держать в голове весь проект сразу. Подход вашего друга позволяет десятку джуниоров работать над проектом, вообще не представляя, зачем это все пишется. Как результат — получается, что функция на верхнем уровне выполняется год с небольшим и теряет память, потому что один из джуниоров написал рекурсивный вызов своей функции, которая использует функция другого джуниора с временем выполнения O(n^3)/


    1. ApeCoder
      30.11.2018 06:44

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

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


  1. ApeCoder
    30.11.2018 10:04
    +4

    Картинко
    image


  1. dzsysop
    30.11.2018 19:46

    Судя по факту что количество коментов продолжает нарастать день ото дня автор 100% прав!
    «Приверженцы» таки никогда не поймут друг друга и топик явно тянет на холиварный.


    1. VolCh
      30.11.2018 20:56

      Понять оппонента не сложно. Сложно убедить его, что он не прав, или убедить себя, что ты не прав.