Які переваги безпеки системи типу?


47

У JavaScript: Добрі частини Дугласа Крокфорда, він згадує у своїй главі про спадщину,

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

Отже, перш за все, що насправді є безпекою? захист від корупції даних, або хакерів, або несправностей системи тощо?

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


Я не впевнений, що системи типів надають якусь користь для некомпільованої мови, але, як довгостроковий користувач скомпільованих мов, я вважаю, що компільовані мови при ретельному перевірці типу є ефективними у запобіганні багатьох видів неоднозначного, невизначеного чи неповного коду від пройти етап "складання". Я думаю, ви могли б сказати, що підказки типу та система Lint є цінними для веб-сценаріїв (JavaScript), і якщо так, я впевнений, що ми їх побачимо достатньо. Дарт кого? Динамічні мови, такі як Python, здаються не гіршими через відсутність системи статичного типу.
Warren P

1
Сьогодні ми розуміємо, що введення тексту має бути поведінковим, а не структурним. На жаль, більшість сучасних мов програмування не мають можливості стверджувати поведінку типу ( див. Це питання для приємного читання). Це робить систему типу досить марною у більшості випадків, тим більше, що прості помилки типу, про які тут згадуються відповіді, можуть виявити розумний вкладиш, який перевіряє наявність загальних проблем.
Бенджамін Грюнбаум

4
@BenjaminGruenbaum Те, що ваше опис уже існує в таких мовах, як OCaml, статично. Це називається структурним набором тексту, воно насправді досить старе, номінальне введення тексту новіше.
jozefg

2
@BenjaminGruenbaum: ... Що !? Очевидно, що не можна визначити в мовах статичного типу, інакше написати компілятор для цих мов було б неможливо.
BlueRaja - Danny Pflughoeft

6
@BenjaminGruenbaum: Ваші коментарі є цінними, і цей документ є цікавим, але він не випливає з Вашого твердження, що "зазвичай це не можна визначити в статичних мовах, як і Java", оскільки це демонструє, що це можна вирішити в C #, і залишає відкритим питання. чи не можна визначити це на Java. (І в будь-якому випадку, IME, коли компілятор для статично введеної мови не може вирішити, що щось добре набрано, він відкидає його (або не може його скомпілювати), тому невизначуваність - це скоріше роздратування, а не діра в типі, безпека.)
ruakh

Відповіді:


82

Системи типу запобігають помилкам

Системи типу усувають незаконні програми. Розглянемо наступний код Python.

 a = 'foo'
 b = True
 c = a / b

У Python ця програма виходить з ладу; це кидає виняток. Такою мовою, як Java, C #, Haskell , це навіть не є легальною програмою. Ви цілком уникаєте цих помилок, оскільки вони просто неможливі в наборі програм введення.

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

 Definition divide x (y : {x : integer | x /= 0}) = x / y

Тепер система типу гарантує відсутність помилок поділу на 0.

Які помилки

Ось короткий перелік того, які системи помилок можуть запобігти

  1. Помилки поза діапазоном
  2. Ін'єкція SQL
  3. Узагальнення 2, багатьох питань безпеки (те, що перевірка забруднення в Perl )
  4. Помилки поза послідовністю (забувши викликати init)
  5. Примушування використовувати підмножину значень (наприклад, лише цілі числа, що перевищують 0)
  6. Підступні кошенята (Так, це був жарт)
  7. Похибки втрати точності
  8. Помилки транзакційної оперативної пам'яті (STM) (для цього потрібна чистота, яка також вимагає типів)
  9. Узагальнення 8, контроль побічних ефектів
  10. Інваріанти в структурі даних (чи збалансоване бінарне дерево?)
  11. Забувши виняток або викинувши неправильний

І пам’ятайте, це також на час компіляції . Не потрібно писати тести зі 100% покриттям коду, щоб просто перевірити наявність помилок типу, компілятор це робить саме для вас :)

Приклад: Введене лямбдальне числення

Добре, давайте розглянемо найпростіші з усіх типів системи, просто набравши лямбда-числення .

В основному є два типи,

Type = Unit | Type -> Type

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

Подумайте над цим, ми можемо використовувати системи типів, щоб гарантувати, що наша програма не працює циклічно, а круто, правда?

Перехід на динамічні типи

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

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

TLDR

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


25
+1 за складання як еквівалент написання безлічі тестів.
Дан Нілі

3
@DanNeely Це просто проілюструвати, що в динамічній мові вам потрібно виконати всі частини коду, щоб виявити помилки, які система типу перевіряє безкоштовно. І мовою, що залежить від введення тексту, ви фактично можете повністю замінити тести типами. Неодноразово потрібно доводити додаткові теореми правильності, хоча
jozefg

3
Якщо ваша система типу довела, що ваша програма повинна припинятися, це (ймовірно) робиться так, доказуючи, що вона обчислює примітивно-рекурсивну функцію. Я думаю, що це круто, але значно менш цікавий клас складності, ніж той, який може вирішити справжня машина Тьюрінга. (Це не означає, що проміжні значення не великі; функція Ackermann є примітивно-рекурсивною…)
Donal Fellows

5
@DonalFellows Функція Ackermann не є примітивною рекурсивною, хоча це загальна обчислювальна функція.
Таймон

4
@sacundim Точно такі мови, як agda, дозволяють перевірити цілісність необов'язково, і в тих рідкісних випадках, коли ви хочете довільну рекурсію, ви можете запитати, це досить гладка система.
jozefg

17

Сама реальність набирається. Не можна додавати довжину до ваг. І хоча ви можете додавати ноги на метри (обидва - це одиниці довжини), вам слід масштабувати хоча б одну з двох. Якщо цього не зробити, це може зірвати вашу місію на Марсі.

У системі typesafe додавання двох довжин, виражених у різних одиницях, було б або помилкою, або спричинило б автоматичне додавання.


15

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

Наприклад, у JavaScript та Python наступна проблема часто може бути спіймана лише під час виконання - і залежно від якості тестування / рідкість умови може фактично зробити її виробництвом:

if (someRareCondition)
     a = 1
else
     a = {1, 2, 3}

// 10 lines below
k = a.length

Хоча сильно набрана мова змусить вас явно вказати, що aце масив, і не дозволить призначити ціле число. Таким чином, жодного шансу aне буде length- навіть у найрідкісніших випадках.


5
І розумний лінк в IDE, як WebStorm JavaScript, може сказати "Можливо невизначене посилання на a.length для числа a". Це не дано нам, маючи явну систему типу.
Бенджамін Грюнбаум

4
1. Статично не сильно 2. @BenjaminGruenbaum Так, але це робиться, переслідуючи графік завдань на задньому плані, подумай про це як про міні-перекладача, намагаючись зрозуміти, куди йдеш. Набагато складніше, ніж коли типи надають це безкоштовно
jozefg

6
@BenjaminGruenbaum: Не плутай неявне / явне із сильним / слабким. Наприклад, Haskell має надзвичайно сильну систему типів, яка пригнічує більшість інших мов, але завдяки певним рішенням щодо дизайну мови також здатна до майже повністю універсального виводу типу, що робить його сильно неявною типізованою мовою з підтримкою явного введення тексту (Це вам слід використовувати, тому що
вивідник

6
"Сильно набрана мова змусить вас чітко заявити, що a - це масив" Це неправильно. Python сильно набраний і цього не вимагає. Навіть статично і сильно набрані мови не вимагають, якщо вони підтримують умови виводу (і більшість основних мов нині, як мінімум, частково).
Конрад Рудольф

1
@BenjaminGruenbaum: Ах, досить справедливо. Незважаючи на це, трапляються випадки, коли жоден статичний аналізатор JS не може виконувати ті ж види перевірки набору тексту, які б сильно набрана мова, вирішивши, що в загальному випадку вимагає вирішення проблеми зупинки. Haskell довелося прийняти досить мало дизайнерських рішень, щоб досягти майже 100% виводу типу, а C # / Scala не може зробити все. Звичайно, у цих випадках це не має значення, оскільки ви можете просто чітко вказати типи - у Javascript це означає, що навіть найкращий статичний аналізатор вже не може перевірити ваш код.
Фоши

5

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

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

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

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


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

@MattFenwick Як ви вважаєте, що є головною перевагою динамічного набору тексту?
GlenPeterson

Типові системи статичного типу за проектом відхиляють багато добре набраних програм. ( альтернатива ) (До речі, моя критика була спрямована лише на 3-й та 4-й пункти.)

4

Вступ

Безпека типу може бути досягнута або зі статичним типом (складений, перевірка статичного типу) та / або мовами виконання (оцінюється, динамічна перевірка типу) мовами. Згідно з Вікіпедією, "... система сильного типу описується як та система, в якій немає можливості помилки типу перевіреного режиму виконання" (ред. Лука Карделлі). Іншими словами, відсутність неперевірених помилок під час виконання позначається як безпека чи безпека типу ... '

Безпека - перевірка статичного типу

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

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

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

Є перевага у використанні мов статичного типу для ефективності та швидкості виконання. Виконання часу не потребує визначення типів під час виконання.

Безпека - перевірка типу виконання

Наприклад, Erlang - це декларативна типова мова, що динамічно перевіряється, що працює на віртуальній машині. Код Erlang може бути складений байтом. Ерланг вважається, мабуть, найважливішою доступною мовою, стійкою до несправностей мовою, і повідомляється, що Ерланг має надійність дев'ятидесятників (99,9999999% або не більше 31,5 мс на рік).

Деякі мови, такі як Common Lisp, не мають статичного типу, але за бажанням можна оголосити типи, що може допомогти підвищити швидкість та ефективність. Слід також зазначити, що багато ширше використовуваних інтерпретованих мов, таких як Python, знаходяться під циклом оцінювання, написаним на мовах статичного типу, таких як C або C ++. Із описаним вище визначенням Commom Lisp і Python вважаються типом безпечними.


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

@ jozefg Добрий момент. Я зміню посаду.
AsymLabs

3
Також не корисно говорити тлумачну мову ... про мовну реалізацію так, але не про саму мову. Будь-яку мову можна інтерпретувати або складати. І навіть після редагування ви використовуєте терміни сильний і слабкий текст.
Есаїлія

3
@jozefg: Я завжди вважав, що сильно набране значення означає, що кожне значення має фіксований тип (наприклад, ціле число, рядок тощо), тоді як слабко введене означає, що значення можна примусити до значення іншого типу, якщо це вважається зручним робити тому. Наприклад, у Python (сильно набраний) 1 + "1"викидає виняток, тоді як у PHP (слабо набраний) 1 + "1"виробляє 2(рядок "1"автоматично перетворюється на ціле число 1).
Джорджіо

1
@Giorgio з таким визначенням, наприклад, Java не набирається сильно. Але в багатьох випадках це стверджується. У цих словах сенсу просто немає. Сильні / слабкі типізовані мають набагато більш точне визначення, як "мені подобається / не мовляю цю мову", як каже jozefg.
Есаїлія

1

переваги безпеки типової системи втрачаються.

Отже, перш за все, що насправді є безпекою? захист від корупції даних, або хакерів, або несправностей системи тощо?

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

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

Тип специфікації, яку можна кодувати як тип, залежить від мови, а точніше, від сили системи типів мови.

Найбільш основний вид специфікації - це гарантія поведінки вводу / виводу функцій та дійсності внутрішньої частини функціонального тіла. Розглянемо заголовок функції

f : (Int,Int) -> String

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

Деякі висловлювання на мові, як, наприклад, блоки блоків, не мають поведінки введення / виводу; тут система типів гарантує, що кожна заява чи заява в блоці є дійсними; тобто застосовує операції з об'єктами правильного виду. Ці гарантії є композиційними.

Крім того, це дає певний стан безпеки пам'яті. Цитата, з якою ви маєте справу, стосується кастингу. У деяких випадках кастинг чудовий, як і 32-розрядний Int до 64-розрядного Int. Однак, як правило, це крах системи типу.

Розглянемо

Foo x = new Foo(3,4,5,6);
f((Int)x,(Int)x);

Через кастинг x перетворюється на Int, тому технічно вище перераховується тип; однак це дійсно перемагає мету перевірки типу.

Одне, що могло б зробити іншу і кращу систему типів, - це заборонити літери (A) x, де x перед випадком є ​​тип B, якщо тільки B не є підтипом (або суб'єктом) А. Ідеї теорії підтипу були використані в безпеці щоб усунути можливість цілих атак переповнення / переливання.

Підсумок

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


1

Ще одна перевага, яка не згадується для системного типу, орієнтується на те, що багато програм читаються більше, ніж написано, і в багатьох випадках система типів може дозволяти задавати багато інформації в стислій формі та легко засвоюється кимсь, хто читає код. Хоча типи параметрів не замінюють описових коментарів, більшість людей швидше прочитають: "int Distance;" абоDistance As Int32ніж читати "Відстань має бути цілим числом +/- 2147483647"; передача дробів може дати непослідовні результати. "Крім того, типи параметрів можуть допомогти зменшити розрив між тим, що відбувається певна реалізація API, порівняно з тим, що абоненти мають право покладатися. Наприклад, якщо певна реалізація JavaScript використовує API його параметри таким чином , який буде примушувати будь-які рядки в числовій формі, може бути неясно , будуть абоненти можуть розраховувати на таку поведінку, або якщо інші варіанти реалізації несправності API могли б , якщо дані рядка. Маючи метод параметр якого визначається як DoubleWould уточнити, що будь-які значення рядка повинні бути примушені абонентом перед передачею; мати метод із перевантаженням, який приймає, Doubleі інший, який приймаєString було б дещо зрозуміліше, що абонентам, що займають рядки, буде дозволено передавати їх як такі.


0

Отже, перш за все, що насправді є безпекою? Захист від пошкодження даних, хакерів, несправностей системи тощо?

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

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

Під "властивістю" вище я маю на увазі певний тип логічної пропозиції, що стосується вашої програми, наприклад, "всі індекси знаходяться в межах масиву". Інші типи властивостей включають: "усі відмічені покажчики дійсні", "ця програма не виконує жодного вводу-виводу" або "ця програма виконує введення-виведення тільки в / dev / null" і т.д. Так само про будь-який тип властивість можна вказати та перевірити тип таким чином, залежно від виразності системи типу.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.