Я думаю, что понимаю строгую типизацию , но каждый раз, когда я ищу примеры слабой типизации, я в конечном итоге нахожу примеры языков программирования, которые просто приводят / конвертируют типы автоматически.
Например, в этой статье под названием « Ввод: сильный и слабый», «Статический и динамический» говорится, что Python строго типизирован, потому что вы получаете исключение, если пытаетесь:
питон
1 + "1"
Traceback (most recent call last):
File "", line 1, in ?
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Однако такое возможно в Java и в C #, и мы не считаем их слабо типизированными только для этого.
Ява
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 # ?.
Ну и дела, это сбивает с толку
Авторы, похоже, подразумевают, что язык, который предотвращает применение определенных операций над значениями разных типов, строго типизирован, а наоборот означает слабо типизированный.
Поэтому в какой-то момент я почувствовал, что мне кажется, что если язык обеспечивает много автоматических преобразований или принуждение между типами (как perl), может оказаться слабо типизированным, тогда как другие языки, которые обеспечивают только несколько преобразований, могут оказаться считается строго типизированным.
Я склонен полагать, однако, что я, должно быть, ошибаюсь в этой интерпретации, я просто не знаю, почему или как это объяснить.
Итак, мои вопросы:
- Что на самом деле означает, что язык действительно слабо типизирован?
- Не могли бы вы привести какие-нибудь хорошие примеры слабой типизации, которые не связаны с автоматическим преобразованием / автоматическим принуждением, выполняемым языком?
- Может ли язык быть типизирован слабо и одновременно строго типизирован?
Ответы:
ОБНОВЛЕНИЕ: Этот вопрос был предметом моего блога 15 октября 2012 года. Спасибо за отличный вопрос!
Это означает, что «этот язык использует систему типов, которая мне неприятна». В отличие от этого, «строго типизированный» язык - это язык с системой типов, которая мне нравится.
Термины по сути бессмысленны, и вам следует избегать их. В Википедии перечислено одиннадцать разных значений для «строго типизированных», некоторые из которых противоречивы. Это указывает на то, что вероятность возникновения путаницы высока в любом разговоре, включающем термин «строго типизированный» или «слабо типизированный».
Все, что вы можете с уверенностью сказать, это то, что обсуждаемый «строго типизированный» язык имеет некоторые дополнительные ограничения в системе типов, либо во время выполнения, либо во время компиляции, что в обсуждаемом «слабо типизированном» языке отсутствует. Чем может быть это ограничение, невозможно определить без дальнейшего контекста.
Вместо того чтобы использовать «строго типизированный» и «слабо типизированный», вы должны подробно описать, какой тип безопасности вы имеете в виду. Например, C # является статически типизированным языком и языком, безопасным для типов, и языком, безопасным для памяти , по большей части, C # допускает нарушение всех трех форм «строгой» типизации. Оператор приведения нарушает статическую типизацию; он говорит компилятору: «Я знаю больше о типе времени выполнения этого выражения, чем вы». Если разработчик ошибается, то среда выполнения выдаст исключение, чтобы защитить безопасность типов. Если разработчик желает нарушить безопасность типов или память, они могут сделать это, отключив систему безопасности типов, сделав «небезопасный» блок. В небезопасном блоке вы можете использовать магию указателя для обработки int как float (нарушая безопасность типов) или для записи в память, которой вы не владеете. (Нарушение безопасности памяти.)
C # налагает ограничения типов, которые проверяются как во время компиляции, так и во время выполнения, тем самым делая его «строго типизированным» языком по сравнению с языками, которые выполняют меньше проверок во время компиляции или меньше проверок во время выполнения. C # также позволяет вам в особых обстоятельствах выполнить обход этих ограничений, что делает его «слабо типизированным» языком по сравнению с языками, которые не позволяют выполнять такой запуск.
Что это на самом деле? Невозможно сказать; это зависит от точки зрения говорящего и его отношения к различным языковым особенностям.
источник
Как уже отмечали другие, термины «строго типизированный» и «слабо типизированный» имеют так много разных значений, что нет единого ответа на ваш вопрос. Однако, поскольку вы специально упомянули Perl в своем вопросе, позвольте мне объяснить, в каком смысле Perl слабо типизирован.
Дело в том, что в Perl нет такой вещи как «целочисленная переменная», «переменная с плавающей запятой», «строковая переменная» или «логическая переменная». На самом деле, насколько может (обычно) пользователь может сказать, нет даже целочисленных, плавающих, строковых или логических значений : все, что у вас есть, - это «скаляры», которые представляют собой все эти вещи одновременно. Так что вы можете, например, написать:
Конечно, как вы правильно заметили, все это можно рассматривать как просто приведение типов. Но дело в том, что в 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 может быть парадигмой. Общее мнение таково: если вы проверяете класс объекта в Perl, вы делаете что-то не так.bless
преобразована в класс, превращая ее в объект этого класса; система классов OO в Perl является несколько ортогональной к описанной выше системе примитивного типа (или отсутствия типа), хотя она также является "слабой" в смысле следования типу утки1 На самом деле, символ обозначает тип доступного значения, так что, например,
@foo
обозначается первый скаляр в массиве$foo[0]
. Смотрите perlfaq4 для более подробной информации.2 Объекты в Perl (обычно) доступны через ссылки на них, но на самом деле редактируется
bless
переменная (возможно, анонимная), на которую указывает ссылка. Однако благословение действительно является свойством переменной, а не ее значения, так что, например, присвоение фактической благословенной переменной другой дает вам ее неглубокую, необдуманную копию. Смотрите perlobj для более подробной информации.источник
В дополнение к тому, что сказал Эрик, рассмотрим следующий код C:
В отличие от таких языков, как Python, C #, Java или еще чего-то, вышеприведенное слабо типизировано, потому что мы теряем информацию о типах. Эрик правильно указал, что в C # мы можем обойти компилятор путем приведения, фактически говоря ему: «Я знаю больше о типе этой переменной, чем вы».
Но даже тогда среда выполнения все равно проверит тип! Если приведение недействительно, система времени выполнения поймает его и выдаст исключение.
При стирании типа этого не происходит - информация о типе выбрасывается. Приведение к
void*
C делает именно это. В этом отношении вышесказанное принципиально отличается от объявления метода C #, такого какvoid f(Object x)
.(Технически, C # также позволяет стирать типы через небезопасный код или сортировку.)
Это так же слабо напечатано, как и получается. Все остальное зависит только от статической и динамической проверки типов, то есть от времени, когда проверяется тип.
источник
void*
пробивает обе проверки типов. Стирание универсального типа - нет, оно только обходит проверки во время компиляции. Это похоже на явное приведение (упомянутое Эриком) в этом отношении.Прекрасный пример взят из статьи Википедии о строгой типизации :
Как правило, строгая типизация подразумевает, что язык программирования накладывает жесткие ограничения на смешивание, которое допускается.
Слабый набор текста
Сильный набор текста
Обратите внимание, что слабый язык ввода может смешивать разные типы без ошибок. Сильный язык типов требует, чтобы входные типы были ожидаемыми типами. В языке строгого типа тип может быть преобразован (
str(a)
преобразует целое число в строку) или приведен (int(b)
).Это все зависит от интерпретации набора текста.
источник
Я хотел бы внести свой вклад в обсуждение с помощью моего собственного исследования по этому вопросу, поскольку другие комментируют и вносят свой вклад, я читал их ответы и следовал их рекомендациям, и я нашел интересную информацию. Как и предполагалось, вероятно, что большая часть этого будет лучше обсуждаться на форуме программистов, так как это выглядит скорее теоретическим, чем практическим.
С теоретической точки зрения, я думаю, что статья Луки Карделли и Питера Вегнера « О понимании типов, абстракции данных и полиморфизма» имеет один из лучших аргументов, которые я прочитал.
Это утверждение, кажется, предполагает, что слабая типизация позволила бы нам получить доступ к внутренней структуре типа и манипулировать им, как если бы это было что-то другое (другой тип). Возможно, что мы могли бы сделать с небезопасным кодом (упомянутым Эриком) или с стертыми указателями типа, упомянутыми Конрадом.
Статья продолжается ...
Таким образом, строгая типизация означает отсутствие ошибок типов, я могу только предположить, что слабая типизация означает обратное: вероятное наличие ошибок типов. Во время выполнения или во время компиляции? Кажется неуместным здесь.
Забавно, что согласно этому определению язык с мощными приведениями типов, такими как Perl, считался бы строго типизированным, потому что система не отказывает, но она имеет дело с типами, приводя их к соответствующим и четко определенным эквивалентам.
С другой стороны, я мог сказать , чем довольствия
ClassCastException
иArrayStoreException
(в Java) иInvalidCastException
,ArrayTypeMismatchException
(в C #) будет указывать на уровень слабо типирования, по крайней мере , во время компиляции? Ответ Эрика, кажется, согласен с этим.Во второй статье под названием « Типичное программирование», приведенной в одной из ссылок, приведенных в одном из ответов на этот вопрос, Лука Карделли углубляется в концепцию нарушений типов:
Таким образом, принуждения типов, подобные тем, которые предоставляются операторами, могут рассматриваться как нарушения типов, но если они не нарушают согласованность системы типов, мы можем сказать, что они не приводят к слабо типизированной системе.
Исходя из этого, Python, Perl, Java или C # не являются слабо типизированными.
Карделли упоминает о двух типах виляций, которые я очень хорошо рассматриваю в случаях действительно слабой типизации:
Такие вещи, которые возможны в таких языках, как C (упомянутый Конрадом) или через небезопасный код в .Net (упомянутый Эриком), действительно подразумевают слабую типизацию.
Я полагаю, что лучший ответ до сих пор - Эрик, потому что определение этих понятий очень теоретическое, и когда речь идет о конкретном языке, интерпретации всех этих понятий могут привести к различным дискуссионным выводам.
источник
Слабая типизация действительно означает, что высокий процент типов может быть неявно приведен в исполнение, пытаясь угадать, что имел в виду кодер.
Строгая типизация означает, что типы не принуждаются или, по крайней мере, принуждаются меньше.
Статическая типизация означает, что типы ваших переменных определяются во время компиляции.
В последнее время многие люди путают «явно напечатанные» с «строго типизированными». «Явно типизированный» означает, что вы объявляете типы ваших переменных явно.
Python в основном строго типизирован, хотя вы можете использовать почти все в логическом контексте, а логические значения можно использовать в целочисленном контексте, и вы можете использовать целое число в контексте с плавающей точкой. Он явно не типизирован, потому что вам не нужно объявлять ваши типы (за исключением Cython, который не совсем python, хотя и интересен). Это также не является статически типизированным.
C и C ++ явно типизированы, статически типизированы и несколько строго типизированы, потому что вы объявляете свои типы, типы определяются во время компиляции, и вы можете смешивать целые и указатели, или целые и двойные числа, или даже приводить указатель на один тип в указатель на другой тип.
Haskell - интересный пример, потому что он явно не типизирован, но также статически и строго типизирован.
источник
Сильная <=> слабая типизация связана не только с континуумом того, сколько или как мало значений автоматически приводятся языком для одного типа данных к другому, но как сильно или слабо фактические значения типизированы . В Python и Java, и в основном в C #, значения имеют свои типы, установленные в камне. В Perl не так много - на самом деле есть лишь несколько разных типов значений для хранения в переменной.
Давайте открывать дела по одному.
питон
В примере Python
1 + "1"
,+
оператор вызывает__add__
для типаint
придав ему строку в"1"
качестве аргумента - однако, это приводит к NotImplemented:Далее интерпретатор пробует
__radd__
строку str:Поскольку это терпит неудачу,
+
оператор терпит неудачу с результатомTypeError: unsupported operand type(s) for +: 'int' and 'str'
. Таким образом, исключение не говорит много о строгой типизации, но тот факт, что оператор+
не принуждает свои аргументы автоматически к одному и тому же типу, указывает на тот факт, что Python не является наиболее слабо типизированным языком в континууме.С другой стороны, в Python
'a' * 5
будет реализован:То есть,
Тот факт, что операция отличается, требует некоторой строгой типизации - однако обратное
*
приведение значений к числам перед умножением не обязательно сделает значения типизированными слабо.Ява
Пример Java
String result = "1" + 1;
работает только потому, что для удобства оператор+
перегружен для строк. Оператор Java+
заменяет последовательность созданиемStringBuilder
(см. Это ):Это скорее пример очень статичной типизации, без фактического принуждения - здесь
StringBuilder
есть метод,append(Object)
который специально используется здесь. В документации сказано следующее:Где
String.valueOf
тогдаТаким образом, это случай абсолютно никакого принуждения со стороны языка - делегирование всех забот самим объектам.
C #
Согласно ответу Джона Скита , оператор
+
даже не перегружен дляstring
класса - сродни Java, это просто удобство, генерируемое компилятором благодаря статической и строгой типизации.Perl
Как объясняет perldata ,
Perl, однако, не имеет отдельного типа данных для чисел, логических значений, строк, нулей,
undefined
s, ссылок на другие объекты и т. Д. - у него есть только один тип для всех этих, скалярный тип; 0 - это скалярное значение, равно как и «0». Скалярная переменная, которая была задана как строка, может действительно измениться на число, и с этого момента вести себя иначе, чем «просто строка», если к ней обращаются в числовом контексте, Скаляр может содержать что угодно в Perl, это столько же, сколько объект в системе. тогда как в Python имена просто ссылаются на объекты, в Perl скалярные значения в именах являются изменяемыми объектами. Более того, система объектно-ориентированного типа приклеена поверх этого: в perl - всего 3 типа данных - скаляры, списки и хэши. Пользовательский объект в Perl - это ссылка (то есть указатель на любой из 3 предыдущих),bless
Вы можете принять любое такое значение и благословить его для любого класса в любой момент, когда захотите.Perl даже позволяет вам изменять классы значений по своему усмотрению - это не возможно в Python, где для создания значения некоторого класса вам нужно явно создать значение, принадлежащее этому классу,
object.__new__
или подобное. В Python вы не можете реально изменить сущность объекта после создания, в Perl вы можете делать много всего:таким образом, идентичность типа слабо связана с переменной, и ее можно изменить с помощью любой ссылки на лету. На самом деле, если вы делаете
\$another
не имеет идентификатора класса, хотя\$val
все равно будет давать благословенную ссылку.TL; DR
Слабая типизация в Perl - это гораздо больше, чем просто автоматическое приведение, и дело в том, что сами типы значений не заложены в камень, в отличие от Python, который динамически, но очень строго типизирован. Это питон дает
TypeError
на1 + "1"
это признак того, что язык является строго типизированным, несмотря на то, наоборот один сделать что - то полезное, как в Java или C # не исключает возможности их быть строго типизированных языков.источник
Как выразились многие другие, само понятие «сильная» против «слабой» типизации проблематично.
Как архетип, Smalltalk очень строго типизирован - он всегда вызывает исключение, если операция между двумя объектами несовместима. Однако я подозреваю, что немногие в этом списке назвали бы Smalltalk строго типизированным языком, потому что он динамически типизирован.
Я считаю, что понятие «статический» и «динамический» типирование более полезно, чем «сильный» или «слабый». В статически типизированном языке все типы вычисляются во время компиляции, и программист должен явно объявить, если это не так.
Сравните с динамически типизированным языком, где типизация выполняется во время выполнения. Обычно это требование для полиморфных языков, так что решения о том, является ли операция между двумя объектами законной, не должны приниматься заранее программистом.
В полиморфных, динамически типизированных языках (таких как Smalltalk и Ruby) более полезно думать о «типе» как о «соответствии протоколу». Если объект подчиняется протоколу так же, как другой объект - даже если эти два объекта не имеют общего наследования, миксинов или другого вуду - они рассматриваются системой во время выполнения как один и тот же «тип». Более правильно, объект в таких системах является автономным и может решить, имеет ли смысл отвечать на какое-либо конкретное сообщение, ссылающееся на какой-либо конкретный аргумент.
Хотите объект, который может дать значимый ответ на сообщение «+» с аргументом объекта, который описывает синий цвет? Вы можете сделать это в динамически типизированных языках, но это проблема в статически типизированных языках.
источник
Мне нравится ответ @Eric Lippert , но для ответа на этот вопрос - языки со строгой типизацией, как правило, четко знают типы переменных в каждой точке программы. Языки со слабой типизацией этого не делают, поэтому они могут пытаться выполнить операцию, которая может быть невозможна для определенного типа. Думаю, что самый простой способ увидеть это в функции. C ++:
Известно, что переменная
a
имеет тип string, и любая несовместимая операция будет обнаружена во время компиляции.Python:
Переменная
a
может быть чем угодно, и у нас может быть код, который вызывает недопустимый метод, который будет пойман только во время выполнения.источник