Шукаю роз'яснення щодо очевидних суперечностей щодо слабко набраних мов


178

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

Наприклад, у цій статті під назвою Typing: Strong vs. слабкі, Static vs. Dynamic йдеться про те, що 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 слабо набраний просто тому, що я можу об'єднати рядок у число та viceversa без явного перетворення.

Perl

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

Тож той самий приклад робить Perl слабо набраним, але не Java та C # ?.

Боже, це заплутано введіть тут опис зображення

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

Тому в якийсь момент я відчув спонукання вважати, що якщо мова забезпечує багато автоматичних перетворень, або примус між типами (як perl) може в кінцевому підсумку вважатись слабко набраним, тоді як інші мови, які забезпечують лише кілька конверсій, можуть закінчитися вважається сильно набраним.

Я схильний вважати, що я маю помилку в цій інтерпретації, я просто не знаю, чому і як це пояснити.

Отже, мої запитання:

  • Що насправді означає, що мова справді слабо набрана?
  • Чи можете ви згадати якісь хороші приклади слабкого набору тексту, які не пов'язані з автоматичним перетворенням / автоматичним примусом, здійсненим мовою?
  • Чи може мова бути одночасно слабко набраною та сильно набраною?

8
Сильне порівняно із слабким набором тексту стосується перетворення типів (про що ще це може бути?) Якщо ви хочете прикладу "дуже" слабкої мови, дивіться це: killallsoftware.com/talks/wat .
Wilduck

2
@Wildduck Усі мови забезпечують перетворення типів, але не всі вони вважаються слабо набраними. Мої приклади, показані нижче, демонструють, як програмісти вважають мову слабко набраною на основі тих же прикладів, які можливі для інших мов, які вважаються сильно набраними. Як таке, моє питання все ще переважає. Яка різниця?
Едвін Далорцо

1
Я вважаю, що коротка відповідь полягає в тому, що "Тип" не є бінарним станом. Java та C # набираються більш сильно, але не абсолютно.
Джодрелл

3
Я вважаю, що це краще підходить для Software Engineering .
zzzzBov

4
@Brendan Що з підсумком float та цілого числа? Чи не ціле число примусове до поплавця в Python? Ви б сказали зараз, що Python не дуже сильно набраний?
Едвін Далорцо

Відповіді:


210

ОНОВЛЕННЯ: Це питання було предметом мого блогу 15 жовтня 2012 року. Дякую за чудове запитання!


Що насправді означає, що мова "слабко набрана"?

Це означає, що "ця мова використовує систему типів, яка мені здається неприємною". Навпаки, "сильно набрана" мова - це мова з системою типів, яка мені здається приємною.

Терміни по суті безглузді, і вам слід їх уникати. У Вікіпедії перелічено одинадцять різних значень для "сильно набраних", кілька з яких суперечливі. Це вказує на те, що шанси плутанини, що створюються, високі в будь-якій розмові, що включає термін "сильно набраний" або "слабо набраний".

Все, що ви можете реально сказати з будь-якою впевненістю, - це те, що "сильно набрана" мова, що обговорюється, має додаткові обмеження в системі типів, або під час виконання, або під час компіляції, що "слабо набраної" мови, що обговорюється, не вистачає. Яке може бути таке обмеження, неможливо визначити без подальшого контексту.

Замість використання "сильно набраних" та "слабо набраних", ви повинні детально описати, який тип безпеки ви маєте на увазі. Наприклад, C # - це статично набрана мова та безпечний тип мова для пам'яті мова, здебільшого. C # дозволяє порушити всі три форми "сильного" введення тексту. Оператор литих порушує статичне введення тексту; він каже компілятору "Я знаю більше про тип виконання цього виразу, ніж ви". Якщо розробник помиляється, час виконання викине виняток, щоб захистити безпеку типу. Якщо розробник бажає порушити безпеку типу або безпеку пам’яті, він може це зробити, вимкнувши систему безпеки типу, зробивши «небезпечний» блок. У небезпечному блоці ви можете використовувати магію вказівника для трактування int як поплавця (що порушує безпеку типу) або для запису в пам'ять, якою ви не володієте. (Порушення безпеки пам’яті.)

C # накладає обмеження типу, які перевіряються як під час компіляції, так і під час виконання, тим самим роблячи її "сильно набраною" мовою порівняно з мовами, які роблять менше перевірки часу компіляції або менше перевірки часу виконання. Також C # дозволяє в особливих обставинах виконувати кінцевий пробіг навколо цих обмежень, роблячи це "слабко набраною" мовою порівняно з мовами, які не дозволяють вам робити такий кінцевий цикл.

Що це насправді? Неможливо сказати; це залежить від точки зору мовця та їхнього ставлення до різних мовних особливостей.


14
@edalorzo: Вона базується на смаку та особистих думках щодо (1), які аспекти теорії типів є актуальними, а які не мають значення, і (2) чи потрібна мова для застосування чи просто заохочення обмежень типу. Як я вже вказував, можна обґрунтовано сказати , що C # є строго типізований , тому що вона дозволяє і заохочує статичну типізацію, і можна було б так само , як розумно сказати , що вона слабо типізованим , тому що допускає можливість порушити безпеку типу.
Ерік Ліпперт

4
@edalorzo: Що стосується складання, знову ж таки, це питання думки. Компілятор мови складання не дозволить вам перемістити 64-бітний подвійний з стека в 32-розрядний регістр; це дозволить вам перемістити 32-бітний покажчик на 64-бітний подвійний з стека в 32-розрядний регістр. У цьому сенсі мова є "безпечною для типу" - вона накладає обмеження на законність програми на основі типової класифікації даних. Чи є це обмеження "сильним" чи "слабким" - це питання думки, але це, очевидно, обмеження.
Ерік Ліпперт

2
Я думаю, що зараз я бачу ваше уявлення, що справді слабко набрана мова повинна бути абсолютно нетипізованою або однотипною, що в реальному житті практично неможливо. Як така, будь-яка мова має певне визначення типів, які є безпечними, і залежно від кількості дірок, які мова надає для порушення чи маніпулювання своїми даними чи типами даних, ви можете, вважаючи, більш-менш слабко введені, можливо навіть у лише певні контексти.
Едвін Далорцо

7
@edalorzo: Правильно. Наприклад, нетипізоване обчислення лямбда приблизно настільки ж слабо набране, як ви можете отримати. Кожна функція - це функція від функції до функції; будь-які дані можуть передаватися до будь-якої функції без обмежень, оскільки все "одного типу". Дійсність виразу в нетиповому обчисленні лямбда залежить тільки від його синтаксичної форми, а не від семантичного аналізу, який класифікує певні вирази як такі, що мають певні типи.
Ерік Ліпперт

3
@Mark Я дав би йому ще +1 за прогноз, що кожен надасть різні тлумачення з цього приводу. Це "слабо типізоване", здається, "міфічне поняття" або "міська легенда", всі це бачили, але ніхто не може довести, що воно існує :-)
Едвін Далорцо

64

Як зазначали інші, терміни "сильно набрані" та "слабо набрані" мають стільки різних значень, що на ваше запитання немає однозначної відповіді. Однак, оскільки ви спеціально згадали 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 може внутрішньо мати одночасно і рядок, і числове значення, як, наприклад,$foo другого рядка вище.

Зворотний бік усього цього полягає в тому, що, оскільки змінні 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, а не його зміст. (Крім того, є кілька інших дивних типів, наприклад, шрифти та ручки вводу / виводу, які ви не часто бачите.)

Також невеликий хит у цьому приємному дизайні - це існування еталонних типів, які є особливим видом скалярів (і які можна відрізнити від звичайних скалярів, використовуючи refоператор). Можна використовувати посилання як звичайні скаляри, але їхні рядкові / числові значення не особливо корисні, і вони, як правило, втрачають свою особливу орієнтирність, якщо ви модифікуєте їх за допомогою звичайних скалярних операцій. Також будь-яка змінна Perl 2 може бутиbless редагувати до класу, перетворюючи її на об'єкт цього класу; система класу ОО в Перлі дещо ортогональна описаній вище системі примітивного типу (або безхарактерності), хоча вона також "слабка" в сенсі слідування вводу качкипарадигма. Загальна думка полягає в тому, що якщо ви виявите, що ви перевіряєте клас об’єкта в Perl, ви щось робите не так.


1 Власне, sigil позначає тип значення, до якого звертаються, так що, наприклад, @fooпозначається перший скаляр у масиві $foo[0]. Дивіться perlfaq4 для більш детальної інформації.

2 До об'єктів у Perl (як правило) можна отримати доступ через посилання на них, але насправді виходить blessзмінна (можливо, анонімна) змінна, на яку посилаються опорні точки. Однак благословення дійсно є властивістю змінної, а не її значення, так що, наприклад, присвоєння фактичної блаженної змінної іншій просто дає вам дрібну копію її. Дивіться perlobj для більш детальної інформації.


19

Окрім того, що сказав Ерік, врахуйте наступний код C:

void f(void* x);

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

На відміну від таких мов, як Python, C #, Java чи щось подібне, вищезазначене слабо набрано, оскільки ми втрачаємо інформацію про тип. Ерік правильно зазначив, що в C # ми можемо обійти компілятор шляхом кастингу, фактично сказавши йому «Я знаю більше про тип цієї змінної, ніж ти».

Але навіть тоді час виконання все одно перевірятиме тип! Якщо кастинг недійсний, система виконання виконує його та викине виняток.

При стиранні типу цього не відбувається - інформація про тип викидається. Литий void*в C робить саме це. У зв'язку з цим вищезазначене принципово відрізняється від декларації методу C #, наприклад void f(Object x).

(Технічно C # також дозволяє видалити тип стирання за допомогою небезпечного коду або маршалінгу.)

Це настільки ж слабо набрано, як це стає. Все інше - лише питання статичної та динамічної перевірки типу, тобто часу, коли тип перевіряється.


1
+1 Добре, що тепер ви змусили мене думати стирання типу як особливість, яка також може означати "слабку типовість". Також у Java є стирання типу, і під час виконання система типів дозволить вам порушити обмеження, які компілятор ніколи не затвердив. Приклад С відмінно ілюструє точку.
Едвін Далорцо

1
Домовились, є шари до цибулі, або до пекла. Це здається більш значущим визначенням слабкості типу.
Джодрелл

1
@edalorzo Я не думаю, що це зовсім не те саме, оскільки, хоча Java дозволяє обійти компілятор, система типового режиму виконує порушення. Тож система типу виконання Java сильно набрана в цьому плані (є винятки, наприклад, коли відображення може бути використане для обходу контролю доступу).
Конрад Рудольф

1
@edalorzo Ви можете лише обійти компілятор таким чином, а не систему виконання. Важливо усвідомити, що такі мови, як Java та C # (і певною мірою також C ++), мають типову систему, яка забезпечується двічі: один раз під час компіляції та один раз під час виконання. void*пробивається через обидва типи перевірок. Зниження загального типу не робить, воно лише обходить перевірки часу компіляції. Це точно як явні касти (згадані Еріком) у цьому плані.
Конрад Рудольф

1
@edalorzo Повторюйтесь у вашій сумніві: ми не повинні. Відмінність вільне. І так, стирання типу робить Java слабкою набраною в цьому плані. Моя думка полягала в тому, що навіть при загальному стиранні типу ви все одно не можете обійти перевірки типу виконання, якщо ви також не використовуєте відображення .
Конрад Рудольф

14

Ідеальний приклад виходить зі статті Вікіпедії «Сильне друкування» :

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

Слабкий набір тексту

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)).

Все це залежить від тлумачення набору тексту.


3
Але це призводить до суперечливих прикладів, наведених у питанні. Сильно набрана мова може включати неявну примус, який означає, що (або обидва) ваших двох прикладів "Помилка типу" автоматично перетворюються на відповідні з двох інших прикладів, але загалом ця мова все ще сильно набрана.
Марк Херд

3
Правда. Я думаю, ви можете сказати, що різний ступінь сильного набору тексту та слабкого набору тексту. Неявне перетворення може означати, що мова менш сильно набрана, ніж мова, яка не робить неявного перетворення.
SaulBack

4

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

З теоретичної точки зору, я вважаю, що стаття Лука Карделлі та Пітера Вегнера під назвою « Розуміння типів, абстрагування даних та поліморфізм» є одним з найкращих аргументів, які я прочитав.

Тип може розглядатися як набір одягу (або бронежилет), який захищає основне нетипізоване представлення від довільного чи ненавмисного використання. Він забезпечує захисне покриття, яке приховує основне зображення і обмежує спосіб взаємодії об'єктів з іншими об'єктами. У нетипізованій системі нетипізовані об'єкти є голими , оскільки базове представлення виставляється для того, щоб усі бачили. Порушення системи типу передбачає зняття захисного набору одягу та функціонування безпосередньо на оголеному зображенні.

Це твердження, здається, говорить про те, що слабке введення тексту дозволило б нам отримати доступ до внутрішньої структури типу і маніпулювати нею, ніби це було щось інше (іншого типу). Можливо, що ми могли б зробити з небезпечним кодом (згаданий Еріком) або зі стертими на екрані вказівниками c, згаданими Конрадом.

Стаття продовжується ...

Мови, в яких усі вирази відповідають типу , називаються сильно набраними мовами. Якщо мова сильно набрана, її компілятор може гарантувати, що програми, які вона приймає, виконуватимуться без помилок типу. Взагалі, ми повинні прагнути до сильного набору тексту та приймати статичне введення тексту, коли це можливо. Зауважте, що кожна статично набрана мова сильно набрана, але зворотна справа не обов'язково відповідає дійсності.

Як таке, сильне введення тексту означає відсутність помилок типу, я можу лише припустити, що слабке введення означає навпаки: ймовірність наявності помилок типу. Під час виконання або час компіляції? Здається, тут не має значення.

Смішна річ, згідно з цим визначенням, мова з потужними коерционами типу, як Perl, вважатиметься сильно набраною, оскільки система не працює, але вона має справу з типами, примушуючи їх до відповідних і чітко визначених еквівалентів.

З іншого боку, я міг сказати , чим забезпечення ClassCastExceptionі ArrayStoreException(в Java) і InvalidCastException, ArrayTypeMismatchException(в C #) буде вказувати на рівень слабо типування, по крайней мере , під час компіляції? Відповідь Еріка, схоже, погоджується з цим.

У другій статті під назвою " Типне програмування", що міститься в одній із посилань, наведеній в одній з відповідей у ​​цьому питанні, Лука Карделлі заглиблюється в поняття порушення типу:

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

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

Таким чином, тип примусів, таких як оператори, що надаються операторами, можна вважати порушеннями типу, але якщо вони не порушують узгодженість системи типів, можна сказати, що вони не призводять до слабо типізованої системи.

Виходячи з цього, Python, Perl, Java або C # не набираються слабко.

Карделлі згадує два типи ушкоджень, які я дуже добре вважаю випадками справді слабкого набору тексту:

Арифметична адреса. При необхідності повинен бути вбудований (незвуковий) інтерфейс, який забезпечує адекватні операції над адресами та перетворення типів. У різних ситуаціях залучаються покажчики в купу (дуже небезпечні для переміщення колекторів), вказівники на стек, вказівники на статичні ділянки та вказівники в інші адресні простори. Іноді індексація масиву може замінити арифметику адреси. Відображення пам'яті. Це передбачає розгляд ділянки пам'яті як неструктурованого масиву, хоча він містить структуровані дані. Це характерно для розподільників пам'яті та колекторів.

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

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


4

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

Сильне введення означає, що типи не примусові або принаймні менше примусові.

Статичне введення означає, що типи змінних визначаються під час компіляції.

Багато людей останнім часом плутають "явно набране" з "сильно набраним". "Явно набрано" означає, що ви явно оголошуєте типи змінних.

Python здебільшого сильно набраний, хоча ви можете використовувати майже будь-що в булевому контексті, а булеві можна використовувати в цілому контексті, а ви можете використовувати ціле число в контексті float. Це не явно набрано, тому що вам не потрібно декларувати свої типи (за винятком Cython, який не зовсім python, хоч і цікавий). Він також не набраний статично.

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

Haskell - цікавий приклад, тому що він не явно набраний, але він також статично і сильно набраний.


+1 Тому що мені подобається введений термін "явно набраний", який класифікує такі мови, як Java та C #, де вам потрібно чітко оголосити типи та відрізнити їх від інших мов статичного типу, таких як Haskell та Scala, де умовивідник відіграє важливу роль, і це, як правило, як ви говорите, бентежить людей, і змушує їх повірити, що ці мови динамічно набираються.
Едвін Далорцо

3

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

Давайте відкриємо справи по черзі.


Пітон

У прикладі 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(див. Це ):

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

Як пояснює перлдата ,

Perl має три вбудовані типи даних: скаляри, масиви скалярів та асоціативні масиви скалярів, відомі як "хеші". Скаляр - це одна рядок (будь-якого розміру, обмежена лише наявною пам'яттю), числом або посиланням на щось (про що мова піде в perlref). Нормальні масиви - це упорядковані списки скалярів, індексованих числом, починаючи з 0. Хеші - це не упорядковані набори скалярних значень, індексовані відповідним рядовим ключем.

Однак Perl не має окремого типу даних для чисел, булевих рядків, рядків, нулів, undefineds, посилань на інші об'єкти тощо - він просто має один тип для цих усіх, скалярний тип; 0 - скалярне значення стільки, скільки "0". Скалярна змінна, яка була встановлена ​​як рядок, може насправді змінити число, а звідти вести себе інакше, ніж "просто рядок", якщо до нього звертаються в числовому контексті. Скаляр може вмістити в Perl що завгодно, він є настільки об'єктом, скільки існує в системі. тоді як в Python назви просто відносяться до об'єктів, у 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, який динамічно, але дуже сильно набраний мовою. Це пітон дає TypeErrorна 1 + "1"це ознака того, що мова є строго типізований, незважаючи на те, навпаки один зробити що - то корисне, як в Java або C # не виключає можливості їх бути строго типізованих мов.


Це зовсім заплутано. Те, що змінні Perl 5 не мають типів, не має ніякого відношення до значень , які завжди мають тип.
Джим Балтер

@JimBalter добре, так, значення має тип у тому, що це рядок або число, і воно може поводитися по-різному в якомусь контексті залежно від того, чи містила скалярна змінна рядок або число; але значення, що міститься в змінній, може змінювати тип, просто відкриваючи змінну, а оскільки саме значення живе у змінній, самі значення можуть вважатися змінними між типами.
Антті Хаапала

Значення не змінюють типів - це непослідовно; значення завжди має тип . Значення, яке містить змінна, може змінюватися. Зміна з 1 на "1" - це настільки ж зміна вартості, як і зміна з 1 на 2.
Джим Балтер

Слабо типізована мова, така як Perl, дозволяє колишньому типу зміни значення відбуватися неявно залежно від контексту. Але навіть C ++ дозволяє такі неявні перетворення через визначення операторів. Слабкий набір тексту - дуже неформальна властивість і насправді не є корисним способом опису мов, як зазначив Ерік Ліпперт.
Джим Балтер

PS Можна показати, що навіть у Perl <digits> та "<digits>" мають різні значення, а не лише різні типи. Perl змушує <digits> і "<digits>", мабуть, мати однакове значення в більшості випадків через неявні перетворення , але ілюзія не є повною; наприклад "12" | "34" - це 36, тоді як 12 | 34 є 46. Іншим прикладом є те, що "00" чисельно дорівнює 00 у більшості контекстів, але не в булевому контексті, де "00" є істинним, але 00 - хибним.
Джим Балтер

1

Як висловили багато інших, все поняття "сильний" проти "слабкого" введення тексту є проблематичним.

Як архетип, Smalltalk дуже сильно набраний - він завжди створюватиме виняток, якщо операція між двома об'єктами несумісна. Однак я підозрюю, що мало хто з цього списку назвав би Smalltalk сильно набраною мовою, оскільки вона динамічно набирається.

Я вважаю, що поняття "статичне" проти "динамічне" введення більш корисне, ніж "сильне" проти "слабке". У статично типізованій мові є всі типи, розібрані під час компіляції, і програміст повинен чітко заявити, якщо це не так.

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

У поліморфних, динамічно набраних мовах (наприклад, Smalltalk та Ruby) корисніше думати про "тип" як "відповідність протоколу". Якщо об'єкт підкоряється протоколу так само, як це робить інший об'єкт - навіть якщо ці два об'єкти не поділяють жодного спадку, міксіну чи іншого вуду, система вважається тим самим "типом". Більш правильно, об'єкт у таких системах є автономним, і він може вирішити, чи є сенс відповідати на якесь конкретне повідомлення, що стосується якогось конкретного аргументу.

Хочете об'єкт, який може зробити якусь змістовну відповідь на повідомлення "+" аргументом об'єкта, який описує колір синій? Це можна зробити на динамічно набраних мовах, але це біль у мовах, що мають статичний тип.


3
Я думаю, що концепція динамічного проти статичного введення тексту не обговорюється. Хоча я мушу сказати, що я не вірю, що поліморфізм так чи інакше перешкоджає мовам статичного типу. Зрештою, система типів перевіряє, чи дана операція застосовна до даних операндів, під час виконання або під час компіляції. Також інші форми поліморфізму, як параметричні функції та класи, дозволяють поєднувати типи в мовах статичного типу способами, які ви описали як дуже складні в порівнянні з динамічно набраними, навіть приємнішими, якщо надається умовивід виду.
Едвін Далорцо

0

Мені подобається відповідь @Eric Lippert , але для вирішення питання - сильно набрані мови зазвичай мають явні знання про типи змінних у кожній точці програми. Слабо набраних мов немає, тому вони можуть спробувати виконати операцію, яка може бути неможливою для певного типу. Думаю, найпростіший спосіб побачити це у функції. C ++:

void func(string a) {...}

Змінна aВідомо, що має тип рядка, і будь-яка несумісна операція буде зафіксована під час компіляції.

Пітон:

def func(a)
  ...

Змінна aможе бути будь-якою, і у нас може бути код, який викликає недійсний метод, який потрапить лише під час виконання.


12
Я думаю, що ви можете заплутати динамічне введення та статичне введення з сильним введенням та слабким введенням тексту. В обох версіях вашого коду системи типу виконання дуже добре знають, що a - це рядок. Просто в першому випадку компілятор може сказати вам, що у другому не може. Але це не робить жодну з цих мов слабко типованою.
Едвін Далорцо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.