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



Давайте раз и навсегда разберёмся во всём том, что вызывает неразбериху при разговорах о системах типов.

Динамическая типизация и отсутствие типизации


Некоторые люди считают, что динамическая система типов (dynamic type system) — это то же самое, что и система типов с отсутствием типизации (untyped type system). Отсутствие типизации означает, что в некоей системе типов нет смысла различать типы. Нет смысла различать типы и в том случае, если в системе типов присутствует всего один тип. Например:

  • В ассемблере единственный тип — строка битов.
  • В лямбда-исчислении единственный тип — функция.

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

Языки, которые не ограничивают область значений переменных, называются нетипизированными языками: в них нет типов, или, что одно и то же, в них есть лишь один универсальный тип, который содержит все значения.
«Системы типов», Лука Карделли

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

  • Нетипизированные языки — программы просто исполняются. Происходит это быстро, без попыток проведения проверок «единообразия форм».
  • Типизированные языки — делается попытка проверки «единообразия формы» — либо во время компиляции, либо во время исполнения программ.

«Системы типов для языков программирования», Бенджамин Пирс

Динамическая и статическая типизация


Динамическая система типов (dynamic type system) — это такая система, в которой типы проверяются динамически (во время исполнения программы). Статическая система типов (static type system) — это система, в которой типы проверяются статически (во время компиляции или транспиляции кода).

Является ли одна из этих систем противоположностью другой? Нет, не является. В одном и том же языке могут применяться обе эти системы типов. На самом деле, в большинстве статических систем типов имеются и динамические проверки типов. В качестве примера можно рассмотреть валидацию операций ввода-вывода (input-output, IO). Представьте себе, что вам нужно прочитать данные, предоставленные пользователем, который должен ввести число. Вы будете проверять, во время исполнения программы, является ли число результатом разбора соответствующей строки (в результате разбора может быть выдано исключение или возвращено нечто вроде NaN). Когда вы проверяете введённые пользователем данные, выясняя, могут ли они рассматриваться как число — вы выполняете динамическую проверку типа.

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

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

Рекомендуется рассматривать статическую систему типов как типы, проверяемые статически. А динамическую систему типов — как типы, проверяемые динамически.

Означает ли применение статических типов знание типов во время компиляции программы?


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

let x = "test";

Оказывается, что парсер знает о том, что "test" — это строка. Делает ли это JavaScript языком со статической типизацией? Нет, не делает.

Постепенная типизация


Система типов с постепенной типизацией (gradual type system) — это статическая система типов, которая позволяет пропускать проверки типов для некоторых частей программы. Например — в TypeScript подобное реализуется с помощью any или @ts-ignore.

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

Надёжные и ненадёжные системы типов


При использовании надёжной системы типов (sound type system) программа, в ходе проверки типов, не будет «одобрена» в том случае, если в этой программе есть ошибки, связанные с типами. Применение ненадёжной системы типов (unsound type system) приводит к тому, что в программе могут присутствовать ошибки, связанные с типами. Не следует, правда, впадать в панику после того, как вы об этом узнали. На практике это может вас не коснуться. Надёжность или корректность (soundness) — это математическое свойство алгоритма проверки типов, которое нуждается в доказательстве. Множество существующих компиляторов (внутри это — системы проверки типов) ненадёжны.

Если вы хотите работать с надёжными системами типов — взгляните на языки программирования семейства ML, в которых используется система типов Хиндли-Милнера (Hindley-Milner).

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

Система типов, которая никогда не отвергает правильные программы, называется полной (complete).

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

Слабая и сильная типизация


Я считаю нецелесообразным использование терминов «слабая типизация» (weak typing) и «сильная типизация» (strong typing). Эти термины неоднозначны, их применение может дать больше путаницы, чем ясности. Приведу несколько цитат.

Эти языки могут называться, образно говоря, языками со слабой проверкой типов (или слабо типизированными языками, как их обычно называют в различных публикациях). Использование в языке слабой проверки типов означает, что некоторые небезопасные операции выявляются статически, а некоторые — нет. «Слабость» проверок типов в языках этого класса серьёзно варьируется.
«Системы типов», Лука Карделли

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

Термины «сильная типизация» и «слабая типизация» чрезвычайно неоднозначны. Вот несколько примеров их использования:

  • Иногда под «сильной типизацией» понимают «статическую типизацию». Такую «подмену» произвести несложно, но лучше, говоря о статической типизации, просто называть её «статической». Дело в том, что большинство программистов вполне однозначно понимают этот термин.
  • Иногда, говоря «сильная типизация», имеют в виду «отсутствие неявного преобразования типов». Например, в JavaScript можно использовать выражения вроде "a" - 1. Это можно назвать образцом «слабой типизации». Но почти все языки дают программисту некие возможности по неявному преобразованию типов, например, поддерживая автоматическое преобразование целых чисел в числа с плавающей точкой в выражениях наподобие 1 - 1.1. На практике большинство специалистов, использующих подобным образом термин «сильная типизация», разграничивают «приемлемые» и «неприемлемые» преобразования типов. Но общепринятой границы между подобными преобразованиями типов не существует. «Приемлемость» и «неприемлемость» преобразований — это субъективная оценка, зависящая от мнения конкретного человека.
  • Иногда языками с «сильной типизацией» называют те языки, в которых никак нельзя обойти правила имеющейся в них системы типов.
  • Иногда «сильная типизация» означает наличие системы типов, которая позволяет безопасно работать с памятью. Язык C — это заметный пример языка, который небезопасно работает с памятью. Например, если xs — это массив из четырёх чисел — C без проблем одобрит код, в котором используются конструкции наподобие xs[5] или xs[1000]. Они позволят обратиться к памяти, которая находится после адресов, выделенных на хранение содержимого массива xs.

«Типы», Гэри Бернар

Нуждаются ли языки со статической типизацией в объявлениях типов?


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

const x = "test";

Система типов знает о том, что "test" — это строка (это знание основано на правилах парсинга кода). Система типов знает и о том, что x — это константа, то есть — значение x нельзя переназначить. В результате может быть сделан вывод о том, что x имеет строковой тип.
Вот ещё один пример (Flow):

const add = (x, y) => x / y
//                        ^ Невозможно выполнить арифметическую операцию так как строка [1] не является числом.
add(1, "2")

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

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

Является ли TypeScript небезопасным языком из-за того, что код, написанный на нём, компилируется в JavaScript-код?


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

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

Тут, если вернуться к мысли о небезопасности TS из-за компиляции в JS, у вас может появиться следующая мысль: «Скомпилированный код выполняется в браузере, JS — язык небезопасный, и в то место, где ожидается строка, он вполне может подставить значение null». Мысль это дельная. Но это, опять же, не даёт повода называть TS небезопасным языком. Для того чтобы TS мог гарантировать безопасность внутри приложения, вам нужно разместить «защитные механизмы» в тех местах, где TS-код взаимодействует с внешним миром. То есть, например, нужно проверять корректность данных, поступающих в программу через механизмы ввода-вывода. Скажем, это может быть проверка того, что вводит пользователь, проверка ответов сервера, проверка данных, читаемых из хранилища браузера и так далее.

Например, роль подобных «защитных механизмов» в Elm играют «порты». В TS для этого можно использовать нечто вроде io-ts.

Соответствующий «защитный механизм» создаёт мост между статической и динамической системами типов.

Вот упрощённый пример:

const makeSureIsNumber = (x: any) => {
  const result = parseFloat(x);
  if (isNaN(result)) {
    throw Error("Not a number");
  }
  return result;
}
const read = (input: any) => {
  try {
    const n = makeSureIsNumber(input);
    // в этой ветке кода n, безусловно, является числом
    // в противном случае мы попали бы в другую ветку кода
    // makeSureIsNumber "гарантирует" то,что n является числом
  } catch (e) { }
}

Правда ли то, что типы нужны лишь для компиляторов?


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

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

Феномен типов существует из-за людей. Типов не существует до тех пор, пока человек не воспринимает нечто в виде «типа данных». Человеческий разум распределяет разные сущности по разным категориям. Типы не имеют смысла без наблюдателя.

Давайте устроим мысленный эксперимент. Подумайте об игре «Жизнь». У вас имеется двумерная сетка, состоящая из квадратных ячеек. Каждая из ячеек может пребывать в двух возможных состояниях. Она может быть «живой» или «мёртвой». Каждая ячейка может взаимодействовать со своими восемью соседями. Это — ячейки, которые граничат с ней по вертикали, по горизонтали, или по диагонали. В процессе нахождения очередного состояния системы применяются следующие правила:

  • «Живая» ячейка, у которой меньше двух «живых» соседей, «умирает», как при низкой плотности населения.
  • «Живая» ячейка, имеющая два или три «живых» соседа, выживает, и попадает в следующее поколение.
  • «Живая» ячейка, у которой больше трёх «живых» соседей, «умирает», как при перенаселённости.
  • «Мёртвая» ячейка, у которой имеется ровно три «живых» соседа, становится «живой», как при воспроизводстве населения.

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

Если какое-то время понаблюдать за «Жизнью», то на поле могут появиться устойчивые структуры наподобие «планера» («glider»).


«Планер»

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

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

Итоги


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

Уважаемые читатели! Как вы думаете, какая система типов могла бы считаться идеальной для целей веб-разработки?

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


  1. nitrosbase
    07.08.2019 13:05
    +2

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


    1. user_man
      07.08.2019 15:12

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

      Думаю со временем вся эта шелуха отомрёт.


    1. NeoCode
      07.08.2019 18:51

      Простите, а что за «аффинные типы»? Я что-то пропустил?


      1. red75prim
        07.08.2019 19:40
        +3

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


  1. sshikov
    07.08.2019 13:48
    +3

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

    Вообще-то нет. Мне кажется, автора тут заклинило по полной.


    1. justboris
      08.08.2019 14:00

      А почему нет?


      На входе у вас неопределенные данные, выполняя проверку вы определяете их тип.


      1. red75prim
        08.08.2019 14:04
        +1

        На входе у нас не неопределенные данные, а значение вполне конкретного типа — строка (или, в зависимости от реализации, последовательность байт, unicode codepoints и т.п.). Если бы на входе было динамически типизированное значение, вот тогда бы имело смысл говорить о проверке его типа.


      1. sshikov
        08.08.2019 14:28

        С какой стати вы определяете их тип, применяя скажем регулярное выражение \d+?


        1. justboris
          09.08.2019 19:14
          -1

          Тип – это понятие расплывчатое.


          Есть int, а есть unsigned int. Есть произвольная строка, а есть enum. То есть при помощи проверок можно сузить тип значения до более узкого.


          1. sshikov
            09.08.2019 19:20

            >Тип – это понятие расплывчатое.

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


            1. VolCh
              09.08.2019 20:12
              +1

              Ну вот что-то вроде


              function isNumberLikeString(value: unknown): value is NumberLikeString {
                return type of value === 'string'  && /\d+/.test(value);
              }
              
              function StringToNumber(value: NumberLikeString): number {
                parseInt(value, 10);
              }
              
              let input = "123";
              
              if (isNumberLikeString(input)) {
                const number: number = StringToNumber(input);
                console.log('Number',  number);
              } else {
                throw Error("TypeError")
              }

              разве не будет динамической проверкой типа?


              1. sshikov
                09.08.2019 20:32

                Ответьте для себя — динамической проверкой типа чего? Тип какой переменной вы тут проверяете? Становится ли строка числом от того, что ее содержимое соответствует регулярному выражению? Будет ли number когда-нибудь какого-то разного типа, или всегда одного?


  1. byme
    07.08.2019 14:47
    +1

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

    Это проеобразование из одного типа в другой. Динамическая проверка типа происходит когда мы кастим переменную типа IFigure в Trigngle.


    1. user_man
      07.08.2019 15:17

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


      1. red75prim
        07.08.2019 16:31
        +1

        Потому что проверка — логическая.

        Это — проверка, но не проверка типов. Тип строка ни в каком случае не равен типу числу. Но некоторые значения типа строка могут быть преобразованы (разными способами) в тип число.


        1. user_man
          08.08.2019 13:16

          Тип «строка» может содержать информацию типа «число». И может не содержать. Отсюда — необходимость проверки типа данных в строке.


          1. red75prim
            08.08.2019 13:57
            +1

            Что является, а что не является строковым представлением числа — внеязыковая информация. А вот, например, правила работы isinstance(obj, str) определёны в языке.


            Так можно дойти до того, что if i > 5 {} — тоже проверка типа, как уже отмечали в обсуждении.


            1. user_man
              09.08.2019 12:41
              -1

              >> Так можно дойти до…

              Это называется — повышение уровня абстракции. Оно полезно. Поэтому так доходить — можно.


              1. red75prim
                10.08.2019 08:44
                +1

                Конкретно эта абстракция называется зависимым типом (dependent type). Система таких типов в общем случае неразрешима, то есть, грубо говоря, невозможно вывести зависимый тип результата функции при её статическом анализе. Для того, чтобы эта абстракция была полезна, язык должен предоставлять возможность явно указывать зависимые типы переменных.


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


                1. user_man
                  10.08.2019 11:58
                  -1

                  >> А мы обсуждаем типы, а не абстракции.

                  Я говорил про «повышение уровня абстракции».


                1. 0xd34df00d
                  10.08.2019 23:47

                  Система таких типов в общем случае неразрешима

                  Э, тайпчекинг разрешим, это вывод типов неразрешим.


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


                  1. Druu
                    11.08.2019 08:11

                    Э, тайпчекинг разрешим

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


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


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


  1. 0xd34df00d
    07.08.2019 16:19

    Открыл статью с ожиданием ментально насладиться, а тут какой-то треш.


    В лямбда-исчислении единственный тип — функция.

    Именно поэтому есть simply-typed lambda calculus, где типов бесконечно много.


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

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


    Является ли одна из этих систем противоположностью другой? Нет, не является.

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


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

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


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

    Значений, но не переменных. JS не знает статически типы переменных.


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

    Ну тут всё стало совсем ясно.


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


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

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


    1. JPEG
      07.08.2019 23:02

      Зашел прочитать этот комментарий.


  1. MaZaAa
    07.08.2019 18:39

    Достала это тупая пропаганда typescript, языки которые созданы с динамический типизацией должны использоваться по назначению, так же как и языки со строгой типизацией. А не так, чтобы пихать везде и всегда типизацию. Залог успеха это только отличная архитектура проекта, а типизирована она или нет, особого значения уже не играет. Если не уметь проектировать архитектуру, то вас никакие фреймворки и никакие типы не спасут, и уж тем более никакие статейки из интернета с «best practice» я смотрю на код нынешних проектов, даже тех которые начали писаться не позднее полу года назад и аж плакать хочется… Они способные к масштабированию и развитию только тем человеком, который их написал, для остальных гораздо быстрее и проще все с нуля переписать нежели сбоку лепить ещё говна на эту кучу говна. Лучше бы пропаганду по тому что надо учиться правильную архитектуру закладывать в приложения устроили и тогда бы и жить всем стало легче.


    1. Pydeg
      07.08.2019 19:59
      +1

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


      1. MaZaAa
        07.08.2019 20:46
        -7

        А это уже напрямую зависит от уровня разработчика, я например таких ошибок уже года 4 точно не совершал, т.к. если я вызываю ту или иную функцию я и так знаю чего она ждет в аргументах, а если не знаю и/или забываю, то просто нажимаю ctrl+на нее и сразу же вижу, что она принимает и как именно работает. Типизация не освобождает от того, что ты должен знать что ты делаешь и чего ты хочешь от той или иной функции или класса. Если такие ошибки у вас в обыденности, то это печальные новости, а вообще я такие ошибки видел с 2015 года вообще в единичных случаях, т.к. как раз начиная с этого периода у меня ни разу в команде джунов не попадалось. Так что если от совершения таких ошибок вас спасает только типизация, то что я могу сказать, значит именно вам она действительно необходима, а мне например по барабану есть она или нет. Я умею извлекать из ее отсутствия максимальную пользу в плане скорости разработки. Но опять же, это все фигня, главное это грамотная архитектура! В реальной жизни web приложения работаю с данными которые приходят из API, а то и из разных API сразу, вот где могут крыться реальные потенциальные проблемы, пришли данные не в том формате и всё, приплыли, а тут уже есть типизация или нету роли никакой не играет. Моя мораль такова, хочешь типизировать всё — пожалуйста, не хочешь — пожалуйста. Это чисто опциональный выбор, а не так как щас многие малюют что мол без TS вообще нереально ничто разработать, якобы большие и сложные проекты без TS не напишешь и т.д. и т.п..., так вот я реализовал и участвовал во многих сложных и огромных проектах, вообще без намека на типизацию, как на фронте, так и на бэке, и только с теми проектами где была нормальная архитектура можно было успешно работать и масштабировать до бесконечности, а остальные пиши пропало, на них благополучно п сей день бешеная текучка кадров, потому что очень мало мазохистов готовы работать с кучкой гуано. Если оверхед по кол-ву кода и по кол-ву затраченного времени возбуждает, то вы созданы чтобы типизировать всё и покрывать тестами каждую строчку кода да ещё и все возможные кейсы описывать в тестах. А я лично ценю своё время, его увы нельзя вернуть и купить за деньги, поэтому мой выбор это не типизировать и не писать unit тесты, благо от проектов где нету денег на тестировщиков в штате я отказываюсь просто, и получается все очень даже шикарно, быстро, просто, надежно и протестировано самым надежным инструментом (ручными тестировщиками)


        1. Pydeg
          07.08.2019 21:25
          +3

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


          1. MaZaAa
            07.08.2019 23:10
            -3

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

            Во первых, это единичные случаи, во вторых параметры которые она принимает находятся все сразу в одном месте, и ни надо ничего изучать, ведь они не названы param1, param2, param3, или названы? :D
            Именно поэтому и говорят, что большие проекты без тс писать боль, просто очень сложно вникать в логику работы всего кода, апи которого ты используешь и одновременно держать в голове столько информации. От этой сложности и спасает типизация, потому что один раз описать типы получается быстрее и проще, чем каждый раз выводить их в голове.

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


            1. Nimtar
              08.08.2019 01:12
              +1

              Так как параметры-то названы?)


              1. MaZaAa
                08.08.2019 09:58

                Обычно все функции называю по принципу f1,f2,f3,..., все переменные var1,var2,var3,..., все классы C1,C2,C3,..., аргументы у функций arg1,arg2,arg3,… И т.п., это же общепризнанное соглашение в сообществе…


                1. Nimtar
                  08.08.2019 14:07

                  Я вам почему-то верю


        1. 0xd34df00d
          07.08.2019 21:47
          +1

          А вы код рефакторите, если не секрет? Или тоже трата денег?


          1. MaZaAa
            07.08.2019 23:24

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


            1. 0xd34df00d
              08.08.2019 01:45
              +2

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

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


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

              Вопрос в том, сколько ты проверяешь.


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


              Торчать целыми сутками в мониторе описывая типы

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


              1. MaZaAa
                08.08.2019 09:46
                -3

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

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

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

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


                1. Pongo
                  08.08.2019 12:31

                  Расскажите, что это за архитектура?


                  1. MaZaAa
                    08.08.2019 12:38

                    Моя собственная, которую с годами я усовершенствовал и усовершенствую по сей день. Конкретно в моем случае у меня есть архитектура для Web Front-end с React.js, и архитектура для бэкенда (для REST API) на Node.js и на бэке я кстати Typescript использую, он там реально полезен на мой взгляд (но в основном для продвинутого автокомплита в IDE конечно), а вот на фронте не использую осознанно.


                    1. Pongo
                      08.08.2019 15:24

                      На бэке значит просто MVC?


                      1. MaZaAa
                        08.08.2019 16:49

                        Не совсем, на rest api MVC не особо ложиться, т.к весь view это просто `JSON.stringify(resultResponseObject)`, так что это ближе к MC, но там где нету какой-то большой логики у меня сразу все в контроллере(запросы в базу и бизнес логика), а там где уже приличная логика, то уже разбиваю ее конечно. Если можно что-то вынести в функции или классы для переиспользования в других местах это разумеется тоже делаю. А в плане архитектуры кода и структуры файлов и папок тоже у всех все по разному.


                1. 0xd34df00d
                  08.08.2019 17:29

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

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


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

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


                  1. MaZaAa
                    08.08.2019 17:38

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

                    Не знаю конечно что у вас за проекты, но у меня как для бэка есть одна на основе которой я разрабатываю все проекта, так же и для фронта. При чем на бэке она подготовлена и под монолит, так и под работу с сервисами(есть обертка для RPC через RabbitMQ), плюс обертки для Postgres, Mysql, redis и т.п. И на бэке у меня есть типизация, но она там ради продвинутого автокомплита.


                    1. 0xd34df00d
                      08.08.2019 17:55

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


                      Вот в последнем без типизации вообще никак.


              1. sshikov
                09.08.2019 19:24

                >А вы, ради интереса просто, какими проектами обычно занимаетесь?
                Да понятно же. Веб. Это довольно типичная манера обобщать то, что имеет место у тебя, на все остальные проекты — даже если их никогда не видел.


        1. Finesse
          08.08.2019 11:45

          если я вызываю ту или иную функцию я и так знаю чего она ждет в аргументах, а если не знаю и/или забываю, то просто нажимаю ctrl+на нее и сразу же вижу, что она принимает и как именно работает:

          Чтобы «ctrl+на неё» работал нормально, нужно описывать аргументы в JSDoc-комментариях вида


          /**
           * @typedef {{}} Options
           * @prop {string} color
           * @prop {number} [size]
           */
          
          /**
           * @param {string} foo
           * @param {Options[]} bar
           */
          function demo(foo, bar) {}

          Иначе «ctrl+на неё» не будет «просто». Вместо JSDoc можно писать TS-аннотации, тратя на них столько же времени и сил, но в замен вы получите автоматическую проверку этих типов:


          interface Options {
            color: string;
            size?: number;
          }
          
          function demo(foo: string, bar: Options[]) {}

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


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


          1. MaZaAa
            08.08.2019 11:49
            -1

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


            1. Finesse
              08.08.2019 11:56

              в нормальных IDE (у меня Webstorm) без JSDoc и вообще без чего-либо все замечательно переходит

              Везде так работает. JSDoc нужен не для этого. Он нужен для того, чтобы описать сигнатуру функции (типы аргументов и возвращаемого значения), чтобы не приходилось переходить к исходному коду функции и изучать его. Если функция описана с помощью JSDoc или TS, вы можете зажать Ctrl, навести курсор на вызов функции и получить её описание.


              По остальным утверждениям я согласен с аргументами других комментаторов.


              1. MaZaAa
                08.08.2019 12:20
                -1

                Не надо ничего изучать, ведь название функции говорит о том, что она делает это раз, названия аргументов говорят о том, что именно эта функция принимает это два, и это в 99% случаев более чем достаточно, но опять же если страдает архитектура и код в целом, то приходится писать явно че к чему и зачем


                1. michael_vostrikov
                  08.08.2019 15:42

                  Допустим вы пришли на проект, и там есть вот такой кусок кода:


                  class Client
                  {
                      public function __construct($kernel, $server, $history, $cookieJar)
                      {
                          $this->kernel = $kernel;
                          parent::__construct($server, $history, $cookieJar);
                      }
                  
                      ...
                  }

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


                  1. MaZaAa
                    08.08.2019 16:55

                    У меня подход к проектам с таким кодом простой, либо переписывать с нуля такую дичь, либо просто ищу другой проект. Потому что я знаю что меня будет тошнить от этого кода и этого прототипа(потому что врятли это можно назвать спроектированным с архитектурной точки зрения) и задача которая при нормальном коде и архитектуре занимает условно час, тут будет занимать минимум х5 по времени + over риски что-то отвалится сбоку. Я не спорю что для таких вот тяп ляп проектов типизация полезна, да. Но для проектов с нормальной архитектурой можно без нее обходится вообще без напрягов.


                    1. michael_vostrikov
                      08.08.2019 18:28

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


                      1. MaZaAa
                        08.08.2019 18:37

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


                        1. michael_vostrikov
                          08.08.2019 21:01

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


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


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


                          1. MaZaAa
                            08.08.2019 21:26

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

                            А вообще я стараюсь придерживаться принципа Kepp It Simple, и делать все просто и очевидно, дабы минимизировать кол-во функций которые требуют того чтобы прочитали их описание или изучили код новые разработчики, то что они будут типизированы в этом им никак не помогут, ну да они узнают на 1 секунду быстрее что аргумент X ждёт integer, а аргумент Y float, но того, что именно она сделает с этим аргументами они и не узнают пока не посмотрят код.


                            1. michael_vostrikov
                              08.08.2019 21:40

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


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


                              1. MaZaAa
                                08.08.2019 23:24

                                Ещё раз, типизация не дает понимаю что конкретно делает та или иная сложная или запутанная функция, она лишь дает понимает вернет ли эта функция bool, строку, какой-то объект и т.п. и что она в себя принимает, но зная это, вы не будете знать все равно что конкретно она вернет и для чего конкретно она служит, коли она сложная и запутанная и все придется посмотреть и разобраться в ее коде, и тут вам типизация мало чем поможет, она просто дополнительный помощник, а не серебряная пуля. Просто если взвесить кол-во времени и дополнительную награможденность кода, то далеко не для всех проектов она реально нужна, более того она зачастую становится вредна т.к бюджеты у бизнеса не всегда резиновые и поэтому продукт стараются вывести в продакшен быстрее. А дальше 2 пути развития:
                                1) Если изначально было заложена хорошая архитектура, то проект замечательно продолжает развиваться и без разницы типизирован или нет;
                                2) Архитектура не было заложен, а было тяп ляп (в основном от не опытности и недостаточной квалификации, слишком велик соблазн нанять разработчиков подешевле) и тут самое интересное, надо делать фичи, фиксить баги и т.п., но с текущим прототипом этом просто боль и страдания и тут происходит ещё одно ветвление:
                                — Проект переписывается по нормальному с нуля и продолжает развиваться (разработчики рады);
                                — Разработчики не рады и ищут новые нормальные проекты, и тут идет постоянная текучка кадров и гуано код разрастается в геометрической прогрессии, а тут уже чтобы удержать хоть как-то разрабов приходится предлагать зарплаты сильно выше рынка, но равно или поздно не остается другого выхода кроме как написать все с нуля;

                                Пъедистал значимости:
                                1) Архитектура (требуются реально сильные разработчики)
                                2) Качество кода (требуются реально сильные разработчики)
                                — это 95% успеха и безпроблемного развития проекта — Далее все остальное, по желанию, бюджету, времени, типизация, тесты, шместы.

                                Учитывая то, что рынок разработчиков перенасыщен новичками и среднечками порой выдающими себя за ведущих специалистов они начинают проект с нуля, понимают что тут уже труба, уходят в другой проект, а остальным потом разгребать за ними. И то что такой типичный проект будет весть протипизирован снизит боль и страдания на 10% и то не факт, может и усугубит их, кривая архитектура + так себе качество кода + нагромождения с описаниями типов = бум, комбо, само счастье вливаться в такой проект.

                                И ещё, если речь о JS, то проектов с TS до сих пор крайне мало и это не спроста.


                                1. michael_vostrikov
                                  08.08.2019 23:46

                                  Ещё раз, типизация не дает понимаю что конкретно делает та или иная сложная или запутанная функция

                                  Еще раз, нам не надо понимать, что конкретно делает та или иная сложная или запутанная функция. Надо понимать, какие конкретно переменные надо создать в месте ее вызова. Чтобы оно потом не упало с ошибкой типа "Unknown method" или "Cannot convert array to string", особенно если это происходит внутри какого-нибудь if только при определенных значениях. И лучше это не вручную контролировать, а компилятором.


                                  Для понимания алгоритма впрочем типизация тоже полезна, но используют ее не за этим.


                                  1. MaZaAa
                                    09.08.2019 10:14

                                    А вы вызываете функции не зная что они делают и для чего они?


                                    1. michael_vostrikov
                                      09.08.2019 11:47

                                      Когда я вызываю (new Client(...))->request(...) или там calculateOrderPrice(...), я знаю, что они делают и для чего они, но мне совершенно неважно, насколько они сложные и запутанные внутри. Но при этом мне важно знать, какие переменные надо создать до этих вызовов, чтобы их туда передать.


                                      1. MaZaAa
                                        09.08.2019 11:51

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

                                        P.S. Вообще тут идет о Typescript, в PHP я знаю что можно опционально типизировать, а в Typescript нельзя (костыли в виде any для всего не в счет, т.к засоряют код). Поэтому сравнение не особо корректное


                                        1. michael_vostrikov
                                          09.08.2019 13:19

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

                                          Нет, не значит. Первая функция делает запрос к определенному URL, вторая делает расчет цены заказа. Это описание работы функций, и оно понятно из их названия. Зачем мне изучать их алгоритм? Мне надо просто передать данные и получить результат.


                                          В противном случае вы не знаете или до конца не знаете как работает функция/класс и типы не освободят от незнания.

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


                                          1. MaZaAa
                                            09.08.2019 13:34

                                            Так если вам все и так понятно из названия, какие могут быть проблемы с типами?) Вы же и так знаете из названия как именно работает функция и что именно она принимает) Это же и есть абстракция от реального мира)


                                            1. michael_vostrikov
                                              09.08.2019 14:48

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

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


                                              Вы же и так знаете из названия как именно работает функция и что именно она принимает

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


                                              1. MaZaAa
                                                09.08.2019 15:18

                                                Вот смотрите, какая разница между этими вариантами, чтобы просто брать и пользоваться ей:
                                                1) Она типизирована;
                                                2) Она описана в документации;
                                                3) Она описана в PHPDoc;
                                                Если для самого первого использования вам так или иначе надо посмотреть что она делает, и какие аргументы и опции у нее есть. Если она типизирована, то вы видите только типы без описания и ничего более, возникает проблема, вы не знаете на 100% предназначение каждого аргумента и каждой опции. А если она описана (и при это уже без разницы типизирована она или нет), то в чем проблема ей пользоваться то? Варианты в стиле «А в друг я передам ей не тот тип в аргументе» не прокатывают, т.к это разряд единичных ошибок(которые отлавливаются сразу же когда вы выполняете код который только что написали) или ошибок начинающих разработчиков.
                                                Напоминаю речь именно о плюс/минус сложных функциях, а не о простых, где названия самой функции и названия аргументов достаточно и они сами по себе являются описанием и документацией.


                                                1. michael_vostrikov
                                                  09.08.2019 16:39

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

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


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

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


                                                  А если она описана (и при это уже без разницы типизирована она или нет), то в чем проблема ей пользоваться то?

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


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


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


                                                  "Calling unknown method calculate()" — как исправить эту ошибку? А если ошибка будет "Argument 1 must be instance of DiscountCalculator"? Есть разница?)


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

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


                                                  Напоминаю речь именно о плюс/минус сложных функциях

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


                                                  1. MaZaAa
                                                    09.08.2019 17:12

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

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

                                                    Подводим итог, типизация выигрывает у сложных функций/классов которые больше вообще нигде и никак не описаны — факт. Но, если сложные функции/классы вообще не протипизированы, но описаны, то увы и ах этого более чем достаточно. Если все так просто и лазурно было, то все бы писали код без багов со времен C, но реальность совсем другая, поэтому типизация это так, просто небольшой помощник для продвинутого автокомплита в IDE, а так же помощник для начинающих разработчиков, которые постоянно пытаются запихнуть в функции совсем не то, что требуется.
                                                    У динамической типзации свои плюсы/минусы
                                                    У статической типизации свои плюсы/минусы
                                                    Победителя НЕТ и быть НЕ МОЖЕТ. Кто хочет тратить гораздо меньше времени на клациние по клавиатуре выбирает динамическую типизацию и сосредотачивается на задачах для бизнеса и архитектуре проекта, кому в кайф написать как можно больше символов в коде, ну и для тех есть выход — статическая типизация.


                                                    1. michael_vostrikov
                                                      09.08.2019 18:50

                                                      Так никакой типизации в таком решении и не будет подавно, и вообще в таком случае не стоит использовать это стороннее решение и написать всё самому.

                                                      Какое стороннее решение? Я не говорил ни про какие сторонние решения, только про ваши примеры. Если в документации типы не описаны то проблема что туда передавать остается, если описаны, то лучше их написать в коде.


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

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


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

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


                                                      Если все так просто и лазурно было, то все бы писали код без багов со времен C

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


                                                      которые постоянно пытаются запихнуть в функции совсем не то, что требуется

                                                      Я вам в предыдущем комментарии привел пример, который от опыта разработчика не зависит. Это изменение требований и связанные с ним изменения в коде. В одном месте поменяли, в другом нет. Ни компилятор ни IDE же не подсказывают. А если вы здесь решили возразить в стиле "вам лишь бы кто-то подсказывал", значит не работали с достаточно большими проектами, где один человек не может знать всё.


                                                      свои плюсы/минусы. Победителя НЕТ и быть НЕ МОЖЕТ

                                                      Верно. И это сильно отличается от вашего изначального категоричного высказывания "Залог успеха это только отличная архитектура проекта, а типизирована она или нет, особого значения уже не играет".


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

                                                      Неверно. Для одноразового "тяп-ляп и в продакшн" возможно да. Для постоянной поддержки и сложных проектов нет. Без типизации вы потратите гораздо больше времени на поиск причин ошибок при изменениях.


                                                      1. leon_nikitin
                                                        09.08.2019 19:27

                                                        надо, просто, человеку написать, какое-нибудь среднее приложение на elm-е. Да, еще, проводя в малые сроки refactoring, в связи с изменением требований. Может тогда поймет, про что речь идет.


                                                    1. leon_nikitin
                                                      09.08.2019 19:19

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

                                                      Да, и, я думаю, наработки с ФП внедряются в языки, позиционирующиеся, как ООП, а не наоборот.


                                                    1. 0xd34df00d
                                                      09.08.2019 19:57

                                                      Если все так просто и лазурно было, то все бы писали код без багов со времен C, но реальность совсем другая, поэтому типизация это так, просто небольшой помощник для продвинутого автокомплита в IDE

                                                      Типизация в С ну очень так себе, мягко скажем.


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

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


                                                1. 0xd34df00d
                                                  09.08.2019 19:55

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

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


                                                  1. MaZaAa
                                                    10.08.2019 11:47

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

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


                                                    1. 0xd34df00d
                                                      10.08.2019 23:48

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


                                              1. leon_nikitin
                                                09.08.2019 15:26

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

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


                                                1. MaZaAa
                                                  09.08.2019 16:34

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


                                                1. michael_vostrikov
                                                  09.08.2019 16:40

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

                                                  Это и есть "для того, чтобы не лесть в реализацию".


                                    1. 0xd34df00d
                                      09.08.2019 19:53

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


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


                                      И это мы ещё не говорили о более сложных предметных областях.


                                1. 0xd34df00d
                                  09.08.2019 19:51

                                  Ещё раз, типизация не дает понимаю что конкретно делает та или иная сложная или запутанная функция

                                  Если тип функции foo : TotalOrdered a => (xs : List a) -> (ys ** Sorted xs ys), то мне не надо на неё смотреть, чтобы понять, что она сортирует массив.


                            1. Druu
                              10.08.2019 06:22

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

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


              1. MaZaAa
                08.08.2019 12:27
                -1

                Плюс того же JSDoc'a ещё в том, что ты можешь описывать только реально сложные функции и классы, а все очевидные явные и так нету смысла описывать, там и так все понятно как 2+2


                1. Finesse
                  08.08.2019 12:53

                  TS тоже не обязывает описывать всё. Можно даже смешивать JS и TS в одном проекте.


                  1. MaZaAa
                    08.08.2019 13:44

                    Да, но тогда смысла вообще нету тащить TS в проект, если тащить TS, то чтобы без единого any все было, а иначе это просто очередная полумера как и почти везде «у нас же TS в проекте, мы же все такие крутые». Я использую TS но только на бэкенде, а не на фронтэнде и мне он нужен реально только для продвинутого автокомплита, а не для проверки типов, поэтому у меня там есть any да, но я и не обманываю сам себя, я честно и прямо говорю что в моем случае это только для продвинутого автокомплита и не более.


      1. worldmind
        08.08.2019 10:21

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


        1. MaZaAa
          08.08.2019 10:38

          Фух, а я то уж думал, что кроме меня никто не придает архитектуре самую высокую степень важности


          1. worldmind
            08.08.2019 10:47

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


            1. MaZaAa
              08.08.2019 10:51

              Именно


            1. 0xd34df00d
              08.08.2019 17:34

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

              Зависит от ее силы. Можно и в SQL-запросах ошибки ловить, скажем.


              1. worldmind
                08.08.2019 17:42

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


                1. 0xd34df00d
                  08.08.2019 17:56

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

                  Отсутствие поля, по которому делается агрегация, в group by, в какую категорию попадает?


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


                  Отсутствие всех значений при вставке для тех колонок, у которых нет default-значения или null?


                  т.к. самое примитивное тестирование их выявит.

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


                  1. worldmind
                    08.08.2019 18:31

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


                    1. 0xd34df00d
                      08.08.2019 20:06

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


                      1. worldmind
                        09.08.2019 09:59

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


  1. NeoCode
    07.08.2019 18:57

    Конечно статическая типизация лучше чем динамическая — просто потому, что статическая типизация может включать в себя динамическую как частный случай (а вот наоборот не получится). Достаточно ввести какой-то тип «dynamic» или «any», для объектов которого будут применяться правила динамической типизации (в том числе с поддержкой со стороны компилятора). Если вы любите динамическую типизацию — просто объявляйте ВСЕ объекты этого типа, и будет как в JS или PHP. Но зато тем, кто предпочитает статическую, не придется плакать горючими слезами при попытках повторного использования такого кода: достаточно будет уяснить по смыслу программы, какого конкретно типа должна быть переменная — заменить в объявлении «any» на этот конкретный тип… и возможно увидеть предупреждения и ошибки компиляции при попытке использовать эту переменную как объект другого типа:)


  1. defuz
    07.08.2019 21:28
    +1

    Нетипизированные языки — программы просто исполняются
    Отличная фраза в статье про типизацию.
    Когда вы проверяете введённые пользователем данные, выясняя, могут ли они рассматриваться как число — вы выполняете динамическую проверку типа.
    То есть нет никакой разницы между проверкой типа и проверкой что строка является валидной репрезентацией числа, да? В статье про типизацию.
    в результате разбора может быть выдано исключение или возвращено нечто вроде NaN
    Ну да, например когда мы парсим целое число, это же так удобно. NaN – это валидный экземпляр типа float. Он не может быть ошибкой парсинга ни целых чисел, ни даже типа float.
    Означает ли применение статических типов знание типов во время компиляции программы? Нет. Оказывается, что парсер знает о том, что”test”— это строка. Делает ли это JavaScript языком со статической типизацией? Нет, не делает.
    Просто отвал башки. Автор задал вопрос и тут же ответил на него, спутав причину и следствие.
    Видите его? По экрану движется «планер». Правда? А теперь давайте немного притормозим. Существует ли этот «планер» на самом деле? Это — просто отдельные квадраты, которые появляются и исчезают. Но наш мозг может воспринимать эту структуру как нечто, объективно существующее.
    Видите текст? Он состоит из осмысленных предложений. Правда? А теперь давайте немного притормозим. Сущевствует ли здесь смысл на самом деле? Или это просто отдельные буквы, которые святятся на экране, а наш мозг воспринимает их как нечто осмысленное?


    1. worldmind
      08.08.2019 10:12
      +1

      Дополню чуток на пальцах про:

      Означает ли применение статических типов знание типов во время компиляции программы

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


      1. defuz
        08.08.2019 11:32

        Да, вы правы, в оригинале было: Isn't static types is when you know types at compile time?