[C#] Прояснение очевидных противоречий относительно слабо типизированных языков


Answers

Как отмечали другие, термины «сильно типизированные» и «слабо типизированные» имеют так много разных значений, что на ваш вопрос нет единого ответа. Однако, поскольку вы конкретно упомянули 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 имеют разные sigils указывающие их тип ( $ для скаляров, @ для массивов, % для хэшей) 1 . Существуют правила принуждения между этими типами, поэтому вы можете написать, например, %foo = @bar , но многие из них довольно сбиты: например, $foo = @bar присваивает длину массива @bar $foo , а не его содержимое , (Кроме того, есть еще несколько странных типов, таких как typeglobs и ручки ввода-вывода, которые вы не часто видите.)

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

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

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

Question

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

Например, в этой статье с именем « Ввод текста: сильный или слабый», «Статический» или «Динамический» говорит, что 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);

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

Perl

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

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

Да, это путает

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

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

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

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

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



I like @Eric Lippert's answer , but to address the question - strongly typed languages typically have explicit knowledge of the types of variables at each point of the program. Weakly typed languages do not, so they can attempt to perform an operation that may not be possible for a particular type. It think the easiest way to see this is in a function. C++:

void func(string a) {...}

The variable a is known to be of type string and any incompatible operation will be caught at compile time.

Python:

def func(a)
  ...

The variable a could be anything and we can have code that calls an invalid method, which will only get caught at runtime.




Прекрасным примером Wikipedia :

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

Слабый ввод текста

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) преобразует целое число в строку) или cast ( int(b) ).

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




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

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

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

Многие люди в последнее время путают «явно типизированные» с «строго типизированными». «Manifestly typed» означает, что вы явно объявляете типы переменных.

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

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

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