Є кілька важливих питань, на які я думаю, що всі існуючі відповіді пропустили.
Слабке введення означає доступ до базового представлення. У C я можу створити покажчик на символи, а потім сказати компілятору, що я хочу використовувати його як вказівник на цілі числа:
char sz[] = "abcdefg";
int *i = (int *)sz;
На платформі з малою ендіанією з 32-бітовими цілими числами це перетворюється i
на масив чисел 0x64636261
і 0x00676665
. Насправді ви навіть можете вказувати самі покажчики на цілі числа (відповідного розміру):
intptr_t i = (intptr_t)&sz;
І, звичайно, це означає, що я можу перезаписати пам'ять у будь-якій точці системи. *
char *spam = (char *)0x12345678
spam[0] = 0;
* Звичайно, сучасна ОС використовує віртуальну пам'ять і захист сторінок, щоб я міг перезаписати пам'ять власного процесу, але нічого про сам C не пропонує такий захист, як може вам сказати кожен, хто коли-небудь кодував, скажімо, Classic Mac OS або Win16.
Традиційний Лісп дозволяв подібні хакери; на деяких платформах клітини з плаваючими і мінусами з двома словами були однотипними, і ви можете просто передати одну функцію, очікуючи іншу, і вона буде "працювати".
Більшість мов сьогодні не настільки слабкі, як C та Lisp, але багато з них все ще дещо пропускні. Наприклад, будь-яка мова OO, яка має неперевірений "downcast", * це тип витоку: ви, по суті, говорите компілятору: "Я знаю, я не дав вам достатньо інформації, щоб знати, що це безпечно, але я впевнений це "коли вся суть системи типу полягає в тому, що у компілятора завжди є достатньо інформації, щоб знати, що безпечно.
* Перевірений понижений режим не робить систему типу мови слабшою лише тому, що вона переміщує чек на час виконання. Якби це було, то підтиповий поліморфізм (він же віртуальний або повністю динамічний виклик функції) був би таким же порушенням типу системи, і я не думаю, що хтось хоче цього сказати.
Дуже мало мов «сценаріїв» слабкі в цьому сенсі. Навіть у Perl або Tcl ви не можете взяти рядок і просто інтерпретувати його байти як ціле число. * Але варто зазначити, що в CPython (і аналогічно для багатьох інших інтерпретаторів для багатьох мов), якщо ви справді стійкі, ви можна використовувати ctypes
для завантаження libpython
, відкидання об'єкта id
до а POINTER(Py_Object)
та примушування системи типу протікати. Незалежно від того, чи робить це тип типу слабким чи ні, залежить від ваших випадків використання - якщо ви намагаєтеся реалізувати пісочницю з обмеженим виконанням мовою для забезпечення безпеки, вам доведеться мати справу з цими видами втечі ...
* Ви можете використовувати таку функцію, як struct.unpack
читати байти та створювати новий int з "як C представляв би ці байти", але це, очевидно, не є герметичним; навіть Хаскелл дозволяє це.
Тим часом неявна конверсія - це справді інша річ від слабкої або нещільної системи типу.
Кожна мова, навіть Haskell, має функції для, скажімо, перетворення цілого числа в рядок або float. Але деякі мови виконають деякі з цих перетворень автоматично для вас, наприклад, в C, якщо ви викликаєте функцію, яка хоче a float
, і ви передаєте її int
, вона перетворюється за вас. Це безумовно може призвести до помилок, наприклад, з несподіваними переповненнями, але вони не є тими ж помилками, які ви отримуєте від слабкої системи. І тут насправді C не є слабкішим; ви можете додати int і float в Haskell, або навіть об'єднати float в рядок, ви просто повинні це зробити більш чітко.
А з динамічними мовами це досить каламутно. У Python чи Perl немає такої речі, як "функція, яка хоче плавати". Але є перевантажені функції, які роблять різні речі з різними типами, і є сильне інтуїтивне відчуття, яке, наприклад, додавання рядка до чогось іншого - це "функція, яка хоче рядок". У цьому сенсі Perl, Tcl і JavaScript, схоже, роблять багато неявних перетворень ( "a" + 1
дає вам "a1"
), тоді як Python робить набагато менше ( "a" + 1
збільшує виняток, але 1.0 + 1
дає вам 2.0
*). Просто важко передати це сенс формальними термінами - чому б не існувало таке, +
що займає рядок і int, коли, очевидно, це роблять інші функції, такі як індексація?
* Насправді, в сучасному Python це можна пояснити з точки зору підтипу OO, оскільки isinstance(2, numbers.Real)
це правда. Я не думаю, що є сенс, у якому 2
є екземпляр типу рядка в Perl або JavaScript ... хоча у Tcl це насправді так, оскільки все є екземпляром рядка.
Нарешті, існує ще одне, повністю ортогональне, визначення "сильного" проти "слабкого" введення тексту, де "сильний" означає потужний / гнучкий / виразний.
Наприклад, Haskell дозволяє визначити тип, який є числом, рядком, списком цього типу або картою від рядків до цього типу, що є ідеальним способом представити все, що можна декодувати з JSON. В Java не можна визначити такий тип. Але принаймні у Java є параметричні (загальні) типи, тож ви можете написати функцію, яка займає Список T і знати, що елементи типу T; інші мови, як-от рання Java, змусили вас використовувати Список об'єктів та знищені дані. Але принаймні Java дозволяє створювати нові типи власними методами; C дозволяє лише створювати структури. І у BCPL цього навіть не було. І так далі до монтажу, де єдині типи мають різну довжину бітів.
Отже, в цьому сенсі система типу Haskell сильніша за сучасну Java, яка сильніша за попередню Java, яка сильніша за C, яка сильніша за BCPL.
Отже, де Python вписується в цей спектр? Це трохи хитро. У багатьох випадках введення качок дозволяє моделювати все, що ви можете зробити в Haskell, і навіть деякі речі, які ви не можете; звичайно, помилки виявляються під час виконання замість часу компіляції, але вони все одно потрапляють. Однак бувають випадки, коли типи качок недостатньо. Наприклад, у Haskell ви можете сказати, що порожній список ints є списком ints, тож ви можете вирішити, що зменшення +
над цим списком має повертати 0 *; у Python порожній список - порожній список; немає інформації про тип, яка допоможе вам вирішити, що зменшити +
над цим.
* Насправді, Haskell не дозволяє вам це зробити; якщо ви викликаєте функцію зменшення, яка не приймає початкове значення у порожньому списку, ви отримуєте помилку. Але система його типу досить потужна, щоб ви могли зробити цю роботу, а Python - ні.