Comments 111
Я бы спросил у автора, почему аффинные типы так называются.
Думаю со временем вся эта шелуха отомрёт.
Вообще-то нет. Мне кажется, автора тут заклинило по полной.
А почему нет?
На входе у вас неопределенные данные, выполняя проверку вы определяете их тип.
На входе у нас не неопределенные данные, а значение вполне конкретного типа — строка (или, в зависимости от реализации, последовательность байт, unicode codepoints и т.п.). Если бы на входе было динамически типизированное значение, вот тогда бы имело смысл говорить о проверке его типа.
Тип – это понятие расплывчатое.
Есть int, а есть unsigned int. Есть произвольная строка, а есть enum. То есть при помощи проверок можно сузить тип значения до более узкого.
Не в этом случае. Речь идет о вводе данных пользователей, и тут у нас на входе строка. Всегда строка. Там нечего проверять, она всегда одного типа. Вы конечно можете называть этот процесс как угодно, но применение регулярки — это не те проверки, которые обычно называют динамической проверкой типа.
Ну вот что-то вроде
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")
}
разве не будет динамической проверкой типа?
Тип переменной input проверяю на предмет совместимости по типу с аргументом функции StringToNumber, можно ли эту переменную "скастовать" в число в терминах предметной области.
x= '123' // тип переменной строковый
x= parseInt('123') // тип переменной становится числовым, и нет никакой проверки на совместимость в момент собственно присваивания.
То есть, из всех проверок именно при вводе есть только проверка, что строка в нужном нам формате. А проверки типа просто не будет — он станет таким, какой вернет функция конвертации.
Я могу конечно допустить, что в каких-то случаях при вводе будут проверки типа, просто если мы вводим ровно одну переменную известного типа — эти проверки тривиальные и очевидные, так как один из типов строка.
Ну то есть, ввод данных — это не тот случай, на котором обычно иллюстрируют проверки типов. Более типичный был бы например x= y+z, где в зависимости от текущих типов y и z может происходить много чего интересного, и операция + будет интерпретироваться сильно по-разному.
Когда вы проверяете введённые пользователем данные, выясняя, могут ли они рассматриваться как число — вы выполняете динамическую проверку типа.
Это проеобразование из одного типа в другой. Динамическая проверка типа происходит когда мы кастим переменную типа IFigure в Trigngle.
Потому что проверка — логическая.
Это — проверка, но не проверка типов. Тип строка ни в каком случае не равен типу числу. Но некоторые значения типа строка могут быть преобразованы (разными способами) в тип число.
Что является, а что не является строковым представлением числа — внеязыковая информация. А вот, например, правила работы isinstance(obj, str)
определёны в языке.
Так можно дойти до того, что if i > 5 {}
— тоже проверка типа, как уже отмечали в обсуждении.
Это называется — повышение уровня абстракции. Оно полезно. Поэтому так доходить — можно.
Конкретно эта абстракция называется зависимым типом (dependent type). Система таких типов в общем случае неразрешима, то есть, грубо говоря, невозможно вывести зависимый тип результата функции при её статическом анализе. Для того, чтобы эта абстракция была полезна, язык должен предоставлять возможность явно указывать зависимые типы переменных.
Если такой возможности в языке нет, то эта абстракция находится только в голове программиста и не имеет отношения к типам в этом языке. А мы обсуждаем типы, а не абстракции.
Я говорил про «повышение уровня абстракции».
Э, тайпчекинг разрешим
Как раз и тайпчекинг для ЗТ неразрешим, т.к. эквивалентен доказательству произвольных теорем.
"Разрешимым" он становится только тогда, когда вы руками доказываете компилятору, что не верблюд :)
Тут надо еще отметить, что есть неразрешимость теоретическая, а есть практическая. Например тайпчек для зт неразрешим практически — ну то есть вы по факту будете постоянно сталкиваться на практике с ситуациями, когда тайпчекер — дурак. С другой стороны, неразрешимость вывода SystemF — теоретическая. Т.е. вы моджете годами писать на ЯП с SystemF и никогда не написать тот самый специфический терм, для которого тип не будет выведен. И важно, что потенциально любая теоретически неразрешимая задача является разрешимой практически.
Так что тайпчекинг эквивалентен проверке доказательства произвольных теорем.
Так проверка доказательств и разрешима только если вы ограничиваетесь при доказательстве конструкциями, которые чекер может прожевать.
Я относительно регулярно утыкаюсь в то, что ghc не поддерживает импредикативный полиморфизм
Как не поддерживает? Это же system f
Если у вас введён тип для чисел больше 5, то да, таким образом вы можете проверить на этот тип и безопасно к нему кастануть.
Ну и где же тут скорость разработки? Чтобы понять что принимает и возвращает функция нужно полностью её прочитать и вникнуть в логику работы
Во первых, это единичные случаи, во вторых параметры которые она принимает находятся все сразу в одном месте, и ни надо ничего изучать, ведь они не названы param1, param2, param3, или названы? :D
Именно поэтому и говорят, что большие проекты без тс писать боль, просто очень сложно вникать в логику работы всего кода, апи которого ты используешь и одновременно держать в голове столько информации. От этой сложности и спасает типизация, потому что один раз описать типы получается быстрее и проще, чем каждый раз выводить их в голове.
Какая разница есть он или нет в хреновой архитектуре, ведь в ней боль в любом случае. Так же как и в хорошей архитектуре нету разницы есть он или нет, потому что в ней боли нету впринципе.
P.S. на трату/не трату денег мне наплевать, мне лишь не наплевать на своё время, которое нельзя вернуть и нельзя купить за деньги. Торчать целыми сутками в мониторе описывая типы и тесты, это не для всех. Кто-то ещё хочет и потратить время с пользой для себя, один раз живём как никак.
А вы, ради интереса просто, какими проектами обычно занимаетесь? А то я вот глупый, выстраивание правильной грамотной™ архитектуры у меня займёт сильно больше времени, чем быстрое прототипирование.
У меня есть готовая архитектура(основа), которую я применяю на всех своих проектах и на ее основе продолжаю дальше разрабатывать и время на это не тратится, т.к. оно уже было потрачено ещё тогда, когда я делал эти наработки.
Вопрос в том, сколько ты проверяешь.
Разумеется быстро, пробежался по основным кейсам и достаточно.
Если я пишу на статически сильно типизированном языке вроде хаскеля, то после рефакторинга и исправлений ругани тайпчекера всё просто работает, даже проверять ничего не надо.
Так это только при условии что весь ваш рефакторинг это просто переименование переменных, а не оптимизация кода в целом
Эх, а у меня что-то так не получается, чтобы одна архитектура на все проекты.
Не знаю конечно что у вас за проекты, но у меня как для бэка есть одна на основе которой я разрабатываю все проекта, так же и для фронта. При чем на бэке она подготовлена и под монолит, так и под работу с сервисами(есть обертка для RPC через RabbitMQ), плюс обертки для Postgres, Mysql, redis и т.п. И на бэке у меня есть типизация, но она там ради продвинутого автокомплита.
Да понятно же. Веб. Это довольно типичная манера обобщать то, что имеет место у тебя, на все остальные проекты — даже если их никогда не видел.
если я вызываю ту или иную функцию я и так знаю чего она ждет в аргументах, а если не знаю и/или забываю, то просто нажимаю 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 не обязательно и увеличивает порог входа в проект, но работать с ним в крупных проектах намного проще (при любой архитектуре).
А с джунами я не работаю и работать не планирую, поэтому мне не нужны никакие полумеры для этого, грамотной архиектуры проекта и кода в целом более чем достаточно.
в нормальных IDE (у меня Webstorm) без JSDoc и вообще без чего-либо все замечательно переходит
Везде так работает. JSDoc нужен не для этого. Он нужен для того, чтобы описать сигнатуру функции (типы аргументов и возвращаемого значения), чтобы не приходилось переходить к исходному коду функции и изучать его. Если функция описана с помощью JSDoc или TS, вы можете зажать Ctrl, навести курсор на вызов функции и получить её описание.
По остальным утверждениям я согласен с аргументами других комментаторов.
Допустим вы пришли на проект, и там есть вот такой кусок кода:
class Client
{
public function __construct($kernel, $server, $history, $cookieJar)
{
$this->kernel = $kernel;
parent::__construct($server, $history, $cookieJar);
}
...
}
Определите пожалуйста по названиям аргументов, что именно эта функция принимает. Либо скажите, как надо изменить названия аргументов, чтобы вы смогли это определить.
Так я потому и спросил — что конкретно вы бы изменили в этом коде? Это вообще-то просто конструктор с параметрами, он в принципе не может быть написан неправильно, потому что по-другому его написать нельзя.
По названиям переменных можно предположить, что это класс, который имитирует поведение браузера. Код из фреймворка Symfony.
Функция, вызывающая другую функцию, это стандартная ситуация для любого алгоритма. Именно поэтому ваше утверждение "Не надо ничего изучать" не соответствует действительности. Надо изучить всю иерархию вызовов, найти место где используется конкретный аргумент, изучить характер использования, и только предположить, а не узнать точно, что туда можно передавать, а что нет.
Даже если эти переменные будут передаваться напрямую в функцию request(), где они используются, ситуация будет та же самая. Вот вы теперь знаете назначение кода, сможете описать, как по-вашему должна выглядеть "нормальная архитектура" для этой функции?
А вообще я стараюсь придерживаться принципа Kepp It Simple, и делать все просто и очевидно, дабы минимизировать кол-во функций которые требуют того чтобы прочитали их описание или изучили код новые разработчики, то что они будут типизированы в этом им никак не помогут, ну да они узнают на 1 секунду быстрее что аргумент X ждёт integer, а аргумент Y float, но того, что именно она сделает с этим аргументами они и не узнают пока не посмотрят код.
Так разговор был не о том, для чего функция служит, а о том, что туда надо передавать и оттуда принимать. Для чего она служит как раз должно быть понятно из названия. "Функция, вызывающая другую функцию" это и есть все остальные самые частые случаи.
Если у вас проект состоит из небольшого количества простых функций, значит это небольшой простой проект. Там можно и без типизации обойтись. А в сложных проектах она помогает, именно поэтому, как вы выразились, ее везде и пихают.
1) Если изначально было заложена хорошая архитектура, то проект замечательно продолжает развиваться и без разницы типизирован или нет;
2) Архитектура не было заложен, а было тяп ляп (в основном от не опытности и недостаточной квалификации, слишком велик соблазн нанять разработчиков подешевле) и тут самое интересное, надо делать фичи, фиксить баги и т.п., но с текущим прототипом этом просто боль и страдания и тут происходит ещё одно ветвление:
— Проект переписывается по нормальному с нуля и продолжает развиваться (разработчики рады);
— Разработчики не рады и ищут новые нормальные проекты, и тут идет постоянная текучка кадров и гуано код разрастается в геометрической прогрессии, а тут уже чтобы удержать хоть как-то разрабов приходится предлагать зарплаты сильно выше рынка, но равно или поздно не остается другого выхода кроме как написать все с нуля;
Пъедистал значимости:
1) Архитектура (требуются реально сильные разработчики)
2) Качество кода (требуются реально сильные разработчики)
— это 95% успеха и безпроблемного развития проекта — Далее все остальное, по желанию, бюджету, времени, типизация, тесты, шместы.
Учитывая то, что рынок разработчиков перенасыщен новичками и среднечками порой выдающими себя за ведущих специалистов они начинают проект с нуля, понимают что тут уже труба, уходят в другой проект, а остальным потом разгребать за ними. И то что такой типичный проект будет весть протипизирован снизит боль и страдания на 10% и то не факт, может и усугубит их, кривая архитектура + так себе качество кода + нагромождения с описаниями типов = бум, комбо, само счастье вливаться в такой проект.
И ещё, если речь о JS, то проектов с TS до сих пор крайне мало и это не спроста.
Ещё раз, типизация не дает понимаю что конкретно делает та или иная сложная или запутанная функция
Еще раз, нам не надо понимать, что конкретно делает та или иная сложная или запутанная функция. Надо понимать, какие конкретно переменные надо создать в месте ее вызова. Чтобы оно потом не упало с ошибкой типа "Unknown method" или "Cannot convert array to string", особенно если это происходит внутри какого-нибудь if только при определенных значениях. И лучше это не вручную контролировать, а компилятором.
Для понимания алгоритма впрочем типизация тоже полезна, но используют ее не за этим.
Когда я вызываю (new Client(...))->request(...)
или там calculateOrderPrice(...)
, я знаю, что они делают и для чего они, но мне совершенно неважно, насколько они сложные и запутанные внутри. Но при этом мне важно знать, какие переменные надо создать до этих вызовов, чтобы их туда передать.
P.S. Вообще тут идет о Typescript, в PHP я знаю что можно опционально типизировать, а в Typescript нельзя (костыли в виде any для всего не в счет, т.к засоряют код). Поэтому сравнение не особо корректное
Противоречите сами себе, если вы знаете что это за функции и как они работают, значит вы знаете какие аргументы и какого типа в них надо передавать.
Нет, не значит. Первая функция делает запрос к определенному URL, вторая делает расчет цены заказа. Это описание работы функций, и оно понятно из их названия. Зачем мне изучать их алгоритм? Мне надо просто передать данные и получить результат.
В противном случае вы не знаете или до конца не знаете как работает функция/класс и типы не освободят от незнания.
Так в том и дело, что освободят. Мне и не надо знать, как они работают, мне надо получить результат. Это и есть абстракция, когда не нужно знать детали реализации. С типами компилятор сам проверит, удовлетворяют ли переменные нужным ограничениям. А в вашем подходе да, надо реализацию изучать.
Так если вам все и так понятно из названия, какие могут быть проблемы с типами?)
Где именно я сказал, что из названия понятно "все" или хотя бы типы аргументов? Я наоборот говорю, что не должно быть необходимости понять "все", а только то что нужно для использования.
Вы же и так знаете из названия как именно работает функция и что именно она принимает
Из названия я знаю что она делает. Абстракция это когда чтобы ее использовать, мне не надо лезть в реализацию и разбираться как именно она работает.
1) Она типизирована;
2) Она описана в документации;
3) Она описана в PHPDoc;
Если для самого первого использования вам так или иначе надо посмотреть что она делает, и какие аргументы и опции у нее есть. Если она типизирована, то вы видите только типы без описания и ничего более, возникает проблема, вы не знаете на 100% предназначение каждого аргумента и каждой опции. А если она описана (и при это уже без разницы типизирована она или нет), то в чем проблема ей пользоваться то? Варианты в стиле «А в друг я передам ей не тот тип в аргументе» не прокатывают, т.к это разряд единичных ошибок(которые отлавливаются сразу же когда вы выполняете код который только что написали) или ошибок начинающих разработчиков.
Напоминаю речь именно о плюс/минус сложных функциях, а не о простых, где названия самой функции и названия аргументов достаточно и они сами по себе являются описанием и документацией.
Если для самого первого использования вам так или иначе надо посмотреть что она делает, и какие аргументы и опции у нее есть.
И в большинстве случаев для этого достаточно названия и типов. Если знать, что переменная $cookieJar
имеет тип CookieJar
, лезть в документацию и выискивать, где там написан тип, не нужно.
Если она типизирована, то вы видите только типы без описания и ничего более, возникает проблема, вы не знаете на 100% предназначение каждого аргумента и каждой опции.
Это единичные случаи. Если их много, и надо постоянно лезть в документацию, то это свидетельствует о плохой архитектуре.
А если она описана (и при это уже без разницы типизирована она или нет), то в чем проблема ей пользоваться то?
Ну раз люди добавляют типизацию туда где ее раньше не было, значит разница есть, даже если вы про нее не знаете.
Если в документации описано ее назначение, но не указано, какие туда передавать типы, то это и есть проблема. А если указаны, почему нельзя указать их в коде? Это дает ту же самую информацию, только еще и автоматические проверки компилятором, а так же не надо каждому программисту лазить в документацию каждый раз когда они работают с этой функцией.
Варианты в стиле «А вдруг я передам ей не тот тип в аргументе» появляются каждый раз, когда меняются типы аргументов в функции уровнем выше, которая вызывает данную, передавая ей часть своих аргументов. Вот как в примере с конструктором Client. Причем с конструкторами проблем еще больше, чем с обычными функциями, потому что упадет вообще потом, далеко от места создания.
"Calling unknown method calculate()" — как исправить эту ошибку? А если ошибка будет "Argument 1 must be instance of DiscountCalculator"? Есть разница?)
которые отлавливаются сразу же когда вы выполняете код который только что написали
Не сразу. Потому что есть такая штука как оператор "if", и часть кода выполняется только при определенных условиях. Есть вызовы в разных местах, и можно поменять саму функцию и код выполнится без ошибок, зато перестанет работать другой код, который не выполняли, но который тоже вызывает эту функцию. Есть передача результатов одних функций в аргументы других, и можно поменять обработку во всех местах вызова, но не поменять в функциях, куда результат вызова передается. С типами ошибка возникает сразу при первом несоответствии, или даже еще до выполнения в подсказке IDE.
Напоминаю речь именно о плюс/минус сложных функциях
Именно поэтому я привел в пример выполнение URL-запроса, где аспектов настолько много, что сделан отдельный класс, и часть из них передается в конструктор. Вы по названиям аргументов и без изучения исходного кода функции ответить на вопрос о типах аргументов не смогли.
Если в документации описано ее назначение, но не указано, какие туда передавать типы, то это и есть проблема.
Так никакой типизации в таком решении и не будет подавно, и вообще в таком случае не стоит использовать это стороннее решение и написать всё самому. Так что, аргумент не засчитан.
Подводим итог, типизация выигрывает у сложных функций/классов которые больше вообще нигде и никак не описаны — факт. Но, если сложные функции/классы вообще не протипизированы, но описаны, то увы и ах этого более чем достаточно. Если все так просто и лазурно было, то все бы писали код без багов со времен C, но реальность совсем другая, поэтому типизация это так, просто небольшой помощник для продвинутого автокомплита в IDE, а так же помощник для начинающих разработчиков, которые постоянно пытаются запихнуть в функции совсем не то, что требуется.
У динамической типзации свои плюсы/минусы
У статической типизации свои плюсы/минусы
Победителя НЕТ и быть НЕ МОЖЕТ. Кто хочет тратить гораздо меньше времени на клациние по клавиатуре выбирает динамическую типизацию и сосредотачивается на задачах для бизнеса и архитектуре проекта, кому в кайф написать как можно больше символов в коде, ну и для тех есть выход — статическая типизация.
Так никакой типизации в таком решении и не будет подавно, и вообще в таком случае не стоит использовать это стороннее решение и написать всё самому.
Какое стороннее решение? Я не говорил ни про какие сторонние решения, только про ваши примеры. Если в документации типы не описаны то проблема что туда передавать остается, если описаны, то лучше их написать в коде.
Подводим итог, типизация выигрывает у сложных функций/классов которые больше вообще нигде и никак не описаны — факт.
Выигрывает даже если написаны. Потому что документация написана где-то там и читает ее только человек, а типы написаны в коде, и читает их в том числе и компилятор.
Но, если сложные функции/классы вообще не протипизированы, но описаны, то увы и ах этого более чем достаточно
Для тех примеров, которые я привел в предыдущем комментарии, этого увы и ах недостаточно. Типы эти ошибки предотвращают, описание в документации нет.
Если все так просто и лазурно было, то все бы писали код без багов со времен C
Это неправильный вывод. Потому что баги бывают связаны не только с типами.
которые постоянно пытаются запихнуть в функции совсем не то, что требуется
Я вам в предыдущем комментарии привел пример, который от опыта разработчика не зависит. Это изменение требований и связанные с ним изменения в коде. В одном месте поменяли, в другом нет. Ни компилятор ни IDE же не подсказывают. А если вы здесь решили возразить в стиле "вам лишь бы кто-то подсказывал", значит не работали с достаточно большими проектами, где один человек не может знать всё.
свои плюсы/минусы. Победителя НЕТ и быть НЕ МОЖЕТ
Верно. И это сильно отличается от вашего изначального категоричного высказывания "Залог успеха это только отличная архитектура проекта, а типизирована она или нет, особого значения уже не играет".
Кто хочет тратить гораздо меньше времени на клациние по клавиатуре выбирает динамическую типизацию
Неверно. Для одноразового "тяп-ляп и в продакшн" возможно да. Для постоянной поддержки и сложных проектов нет. Без типизации вы потратите гораздо больше времени на поиск причин ошибок при изменениях.
Да, и, я думаю, наработки с ФП внедряются в языки, позиционирующиеся, как ООП, а не наоборот.
Я не хочу выполнять код (и, более того, прогонять все его ветви), который только что написал, только для того, чтобы убедиться, что он имеет смысл. Особенно если этот код имеет сайд-эффекты на реальный мир (запросы там куда-нибудь отправляет, например). Именно для этого и нужны системы типов.
Т.е если все типы совпали это означает, что код будет работать именно так, как ожидается, и разумеется нет нужды его проверять верно?
Та же инкапсуляция и сокрытие, не для того, чтобы не лесть в реализацию. Может в ряде случаев и надо залесть в реализацию. Инкапсуляция — для того, чтобы в коде, который использует конструкцию, пользовался только интерфейсам, чтобы можно было безболезненно изменять код.
для того, чтобы в коде, который использует конструкцию, пользовался только интерфейсам
Это и есть "для того, чтобы не лесть в реализацию".
От программиста требуется только понять, что эта сущность должна сделать и какие ей нужны для этого данные.
Обобщенное решение скорее подвид абстракции, один из многих. Отдача наружу только публичного интерфейса или, более общего, запрет работы напрямую, только через публичный контракт- другой. Инкапсуляция — третий. Сокрытие — скорее просто техничекая поддержка второй.
Если вы используете код сторонних библиотек/фреймворков, то у него есть документация как правило, а когда вы сами пишете код, то будьте добры писать его так, что он был очевидным, простым и наглядным
Так вы продемонстрируйте, как этот код написать так, чтобы он был очевидным, простым и наглядным, в чем проблема?
TS тоже не обязывает описывать всё. Можно даже смешивать JS и TS в одном проекте.
А при плохой архитектуре и коде можно понаделать ошибок при полной корректности всех типов.
А вот обнаружить ошибку в запутанной логике спагетти-кода система типов не может.
Нетипизированные языки — программы просто исполняютсяОтличная фраза в статье про типизацию.
Когда вы проверяете введённые пользователем данные, выясняя, могут ли они рассматриваться как число — вы выполняете динамическую проверку типа.То есть нет никакой разницы между проверкой типа и проверкой что строка является валидной репрезентацией числа, да? В статье про типизацию.
в результате разбора может быть выдано исключение или возвращено нечто вроде NaNНу да, например когда мы парсим целое число, это же так удобно. NaN – это валидный экземпляр типа float. Он не может быть ошибкой парсинга ни целых чисел, ни даже типа float.
Означает ли применение статических типов знание типов во время компиляции программы? Нет. Оказывается, что парсер знает о том, что”test”— это строка. Делает ли это JavaScript языком со статической типизацией? Нет, не делает.Просто отвал башки. Автор задал вопрос и тут же ответил на него, спутав причину и следствие.
Видите его? По экрану движется «планер». Правда? А теперь давайте немного притормозим. Существует ли этот «планер» на самом деле? Это — просто отдельные квадраты, которые появляются и исчезают. Но наш мозг может воспринимать эту структуру как нечто, объективно существующее.Видите текст? Он состоит из осмысленных предложений. Правда? А теперь давайте немного притормозим. Сущевствует ли здесь смысл на самом деле? Или это просто отдельные буквы, которые святятся на экране, а наш мозг воспринимает их как нечто осмысленное?
Означает ли применение статических типов знание типов во время компиляции программы
может это проблема перевода, но понятнго, что ответ положительный, видимо имелось ввиду «Означает ли знание типов на стадии компиляции статичность типизации?» вот того ответ нет.
Часто задаваемые вопросы о системах типов