В поисках разъяснений о явных противоречиях в отношении слаботипированных языков - PullRequest
176 голосов
/ 29 марта 2012

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

Например, в этой статье с именем Ввод: сильный или слабый, статический или динамический говорит, что Python строго типизирован, потому что вы получите исключение, если попытаетесь:

Python

1 + "1"
Traceback (most recent call last):
File "", line 1, in ? 
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Однако такое возможно в Java и в C #, и мы не считаем их слабо типизированными только для этого.

Java

  int a = 10;
  String b = "b";
  String result = a + b;
  System.out.println(result);

C #

int a = 10;
string b = "b";
string c = a + b;
Console.WriteLine(c);

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

Perl

$a=10;
$b="a";
$c=$a.$b;
print $c; #10a

Так что тот же пример делает Perl слабо типизированным, но не Java и C #?.

Ну и дела, это сбивает с толку enter image description here

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

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

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

Итак, мои вопросы:

  • Что на самом деле означает, что язык действительно слабо типизирован?
  • Не могли бы вы упомянуть хорошие примеры слабой типизации, которые не связаны с автоматическим преобразованием / автоматическим принуждением, выполняемым языком?
  • Может ли язык быть типизирован слабо и одновременно строго типизирован?

Ответы [ 9 ]

209 голосов
/ 29 марта 2012

ОБНОВЛЕНИЕ: Этот вопрос был темой моего блога 15 октября 2012 года. Спасибо за отличный вопрос!


Что это на самом деле означаетдля языка, который будет "слабо типизирован"?

Это означает, что «этот язык использует систему типов, которая мне неприятна».В отличие от этого, «строго типизированный» язык - это язык с системой типов, которая мне нравится.

Термины по сути бессмысленны, и их следует избегать. Википедия списки одиннадцать различных значений для "строго типизированных", некоторые из которых противоречивы.Это указывает на то, что вероятность возникновения путаницы высока в любом разговоре, включающем термин «строго типизированный» или «слабо типизированный».

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

Вместо того, чтобы использовать «строго типизированный» и «слабо типизированный», вы должны подробно описать, какой тип безопасности вы имеете в виду.Например, C # - это статически типизированный язык и типобезопасный язык и безопасный для памяти язык, по большей части .C # допускает нарушение всех трех форм «строгой» типизации.Оператор приведения нарушает статическую типизацию;он говорит компилятору: «Я знаю больше о типе времени выполнения этого выражения, чем вы».Если разработчик ошибается, то среда выполнения выдаст исключение, чтобы защитить безопасность типов.Если разработчик желает нарушить безопасность типов или память, они могут сделать это, отключив систему безопасности типов, сделав «небезопасный» блок.В небезопасном блоке вы можете использовать магию указателя для обработки int как float (нарушая безопасность типов) или для записи в память, которой вы не владеете.(Нарушение безопасности памяти.)

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

Что это на самом деле?Невозможно сказать;это зависит от точки зрения говорящего и его отношения к различным языковым особенностям.

64 голосов
/ 30 марта 2012

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

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

$foo = "123" + "456";           # $foo = 579
$bar = substr($foo, 2, 1);      # $bar = 9
$bar .= " lives";               # $bar = "9 lives"
$foo -= $bar;                   # $foo = 579 - 9 = 570

Конечно, как вы правильно заметили, все это можно рассматривать как просто принуждение к типу. Но дело в том, что в Perl типы всегда принудительно. На самом деле, пользователю довольно сложно сказать, каким может быть внутренний «тип» переменной: в строке 2 в моем примере выше, спрашивая, является ли значение $bar строкой "9" или числом 9 в значительной степени бессмысленно, поскольку для Perl это одно и то же . Действительно, для скаляра Perl даже возможно внутренне иметь и строку и числовое значение одновременно, как, например, случай для $foo после строки 2 выше.

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

Таким образом, например, Perl имеет и нуждается как в числовом операторе сложения (+), так и в операторе конкатенации строк (.): как вы видели выше, можно добавлять строки ("1" + "2" == "3") или объединить числа (1 . 2 == 12). Аналогично, операторы сравнения чисел ==, !=, <, >, <=, >= и <=> сравнивают числовые значения своих аргументов, а операторы сравнения строк eq, ne, lt, gt, le, ge и cmp сравнивают их лексикографически как строки. Так что 2 < 10, но 2 gt 10 (но "02" lt 10, а "02" == 2). (Имейте в виду, некоторые другие языки, такие как JavaScript, пытаются приспособиться к Perl-подобной слабой типизации, в то время как также выполняет перегрузку операторов. Это часто приводит к уродству, например к потере ассоциативности для +.)

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

Из всего сказанного можно утверждать, что Perl имеет сильных типов; они просто не те типы, которые вы могли бы ожидать. В частности, в дополнение к рассмотренному выше типу «скаляр» Perl также имеет два структурированных типа: «массив» и «хеш». Это очень , отличные от скаляров, до точки, где переменные Perl имеют разные сигилы , указывающие их тип ($ для скаляров, @ для массивов, % для хэшей) 1 . есть правила принуждения между этими типами, так что вы можете написать, например. %foo = @bar, но многие из них с большими потерями: например, $foo = @bar присваивает длину массива @bar $foo, а не его содержимому. (Также есть несколько других странных типов, таких как typeglobs и дескрипторы ввода / вывода, которые вы не часто видите открытыми.)

Кроме того, небольшая разница в этом хорошем дизайне связана с существованием ссылочных типов, которые являются особым видом скаляров (и которые можно отличить от обычных скаляров с помощью оператора ref). Можно использовать ссылки в качестве обычных скаляров, но их строковые / числовые значения не особенно полезны, и они имеют тенденцию терять свою специальную ссылку, если вы изменяете их, используя обычные скалярные операции. Кроме того, любая переменная Perl 2 может быть bless преобразована в класс, превращая ее в объект этого класса; система классов OO в Perl несколько ортогональна к описанной выше системе примитивного типа (или отсутствия типа), хотя она также является "слабой" в смысле следования парадигме типизации утки *1083*. Общее мнение таково: если вы проверяете класс объекта в Perl, вы делаете что-то не так.


1 На самом деле, символ обозначает тип доступного значения, например, первый скаляр в массиве @foo обозначается $foo[0]. См. perlfaq4 для получения более подробной информации.

2 Объекты в Perl (обычно) доступны через ссылки на них, но на самом деле bless ed - это (возможно, анонимная) переменная, на которую указывает ссылка. Однако благословение действительно является свойством переменной, , а не ее значения, например, что присвоение фактической благословенной переменной другой просто дает вам неглубокую, необсуждаемую копию. Подробнее см. perlobj .

19 голосов
/ 30 марта 2012

В дополнение к тому, что сказал Эрик, рассмотрим следующий код C:

void f(void* x);

f(42);
f("hello");

В отличие от таких языков, как Python, C #, Java или еще много чего, вышеприведенный текст слабо типизирован, поскольку мы теряем информацию типа . Эрик правильно указал, что в C # мы можем обойти компилятор путем приведения, фактически говоря ему: «Я знаю больше о типе этой переменной, чем вы».

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

При стирании типа этого не происходит - информация о типе выбрасывается. Приведение к void* в C делает именно это. В этом отношении приведенное выше принципиально отличается от объявления метода C #, например void f(Object x).

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

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

14 голосов
/ 29 марта 2012

Прекрасным примером является статья Википедии о строгом наборе :

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

Слабый набор текста

a = 2
b = "2"

concatenate(a, b) # returns "22"
add(a, b) # returns 4

Сильный набор

a = 2
b = "2"

concatenate(a, b) # Type Error
add(a, b) # Type Error
concatenate(str(a), b) #Returns "22"
add(a, int(b)) # Returns 4

Обратите внимание, что слабый язык ввода может смешивать разные типы без ошибок. Сильный язык типов требует, чтобы входные типы были ожидаемыми типами. В языке строгого типа тип может быть преобразован (str(a) преобразует целое число в строку) или приведен (int(b)).

Все зависит от интерпретации набора текста.

4 голосов
/ 18 октября 2012

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

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

Статическая типизация означает, что типы ваших переменных определяются во время компиляции.

В последнее время многие люди путают «явно типизированный» с «строго типизированным».«Явно типизированный» означает, что вы объявляете типы ваших переменных явно.

Python в основном строго типизирован, хотя вы можете использовать почти все в логическом контексте, а логические могут использоваться в целочисленном контексте, и вы можетеиспользуйте целое число в контексте с плавающей точкой.Он явно не типизирован, потому что вам не нужно объявлять ваши типы (за исключением Cython, который не совсем python, хотя и интересен).Он также не статически типизирован.

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

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

4 голосов
/ 30 марта 2012

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

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

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

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

Статья продолжается ...

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

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

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

С другой стороны, могу ли я сказать, что допустимые значения ClassCastException и ArrayStoreException (в Java) и InvalidCastException, ArrayTypeMismatchException (в C #) указывают на уровень слабой типизации, по крайней мере при компиляции время? Эрик, похоже, согласен с этим.

Во второй статье под названием Typeful Programming , приведенной в одной из ссылок, приведенных в одном из ответов на этот вопрос, Лука Карделли углубляется в концепцию нарушений типов:

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

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

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

Исходя из этого, Python, Perl, Java или C # слабо типизированы.

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

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

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

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

3 голосов
/ 10 августа 2014

Сильная <=> слабая типизация касается не только континуума того, сколько или насколько мало значений автоматически приводится языком для одного типа данных в другой, но насколько сильно или слабо фактические значения набраны. В Python и Java, и в основном в C #, значения имеют свои типы, установленные в камне. В Perl не так много - на самом деле есть лишь несколько различных типов значений для хранения в переменной.

Давайте откроем дела по одному.


Python

В примере Python 1 + "1", оператор + вызывает __add__ для типа int, давая ему строку "1" в качестве аргумента - однако это приводит к NotImplemented:

>>> (1).__add__('1')
NotImplemented

Затем интерпретатор пытается __radd__ из str:

>>> '1'.__radd__(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__radd__'

При сбое оператор + завершается ошибкой с результатом TypeError: unsupported operand type(s) for +: 'int' and 'str'. Таким образом, исключение не говорит много о строгой типизации, но тот факт, что оператор + не приводит свои аргументы автоматически к одному и тому же типу, указывает на то, что Python не является наиболее слабо типизированный язык в континууме.

С другой стороны, в Python 'a' * 5 реализовано :

>>> 'a' * 5
'aaaaa'

То есть

>>> 'a'.__mul__(5)
'aaaaa'

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


Java

Пример Java, String result = "1" + 1;, работает только потому, что для удобства оператор + перегружен для строк. Оператор Java + заменяет последовательность созданием StringBuilder (см. this ):

String result = a + b;
// becomes something like
String result = new StringBuilder().append(a).append(b).toString()

Это скорее пример очень статичной типизации, без фактического принуждения - у StringBuilder есть метод append(Object), который специально используется здесь. В документации сказано следующее:

Добавляет строковое представление аргумента Object.

Общий эффект такой, как если бы аргумент был преобразован в строка по методу String.valueOf(Object) и символы эта строка затем была добавлена ​​к этой последовательности символов.

Где String.valueOf, затем

Возвращает строковое представление аргумента Object. [Возвращает], если аргумент null, то строка, равная "null"; в противном случае возвращается значение obj.toString().

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


C #

Согласно ответу Джона Скита здесь , оператор + даже не перегружен для класса string - сродни Java, это просто удобство, генерируемое компилятором, благодаря как статическим, так и строгий набор текста.


Perl

Как объясняет perldata ,

Perl имеет три встроенных типа данных: скаляры, массивы скаляров и ассоциативные массивы скаляров, известные как «хэши». Скаляр - это отдельная строка (любого размера, ограниченная только доступной памятью), число или ссылка на что-либо (что будет обсуждаться в perlref). Обычные массивы - это упорядоченные списки скаляров, проиндексированные по номерам, начиная с 0. Хэши - это неупорядоченные наборы скалярных значений, проиндексированных по соответствующему строковому ключу.

Perl, однако, не имеет отдельного типа данных для чисел, логических значений, строк, нулей, undefined s, ссылок на другие объекты и т. Д. - у него есть только один тип для всех этих, скалярный тип;0 - это скалярное значение, равно как и «0».Скалярная переменная , заданная в виде строки, может действительно измениться на число, и с этого момента вести себя иначе, чем "просто строка", , если к ней обращаются в числовом контексте .Скаляр может содержать что угодно в Perl, он такой же объект, как и существующий в системе.тогда как в Python имена просто ссылаются на объекты, в Perl скалярные значения в именах являются изменяемыми объектами.Более того, система объектно-ориентированного типа приклеена поверх этого: в perl - всего 3 типа данных - скаляры, списки и хэши.Пользовательский объект в Perl - это ссылка (то есть указатель на любой из 3 предыдущих), bless редактируемый для пакета - вы можете принять любое такое значение и благословить его для любого класса в любой момент, когда захотите.

Perl даже позволяет вам изменять классы значений по своему усмотрению - это не возможно в Python, где для создания значения некоторого класса вам нужно явно создать значение, принадлежащее этому классу, с помощью object.__new__ или подобного,В Python вы не можете реально изменить сущность объекта после создания, в Perl вы можете делать много всего:

package Foo;
package Bar;

my $val = 42;
# $val is now a scalar value set from double
bless \$val, Foo;
# all references to $val now belong to class Foo
my $obj = \$val;
# now $obj refers to the SV stored in $val
# thus this prints: Foo=SCALAR(0x1c7d8c8)
print \$val, "\n"; 
# all references to $val now belong to class Bar
bless \$val, Bar;
# thus this prints Bar=SCALAR(0x1c7d8c8)
print \$val, "\n";
# we change the value stored in $val from number to a string
$val = 'abc';
# yet still the SV is blessed: Bar=SCALAR(0x1c7d8c8)
print \$val, "\n";
# and on the course, the $obj now refers to a "Bar" even though
# at the time of copying it did refer to a "Foo".
print $obj, "\n";

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

my $another = $val;

\$another нет идентификатора класса, даже если \$val все равно даст благословенную ссылку.


TL; DR

Существует гораздо больше о слабой типизации в Perl, чем просто автоматическое принуждение, и больше о том, что типы самих значений не заложены в камень, в отличие от Python, который является динамически, но очень строго типизированным языком.То, что python дает TypeError на 1 + "1", указывает на то, что язык строго типизирован, хотя, наоборот, делает что-то полезное, как в Java или C #, не исключает, что они являются строго типизированными языками.

0 голосов
/ 04 апреля 2012

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

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

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

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

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

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

0 голосов
/ 29 марта 2012

Мне нравится @ ответ Эрика Липперта , но для решения вопроса - строго типизированные языки обычно имеют явное знание типов переменных в каждой точке программы.В слабо типизированных языках этого нет, поэтому они могут пытаться выполнить операцию, которая может быть невозможна для определенного типа.Думаю, самый простой способ увидеть это - это функция.C ++:

void func(string a) {...}

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

Python:

def func(a)
  ...

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

...