Статична типізація як і чому

Статична типізація як і чому

Хоча переваги статичної типізації очевидні, непогано поговорити про них ще раз.

переваги

Причини застосування статичної типізації в об'єктної технології ми перерахували на початку лекції. Це надійність, простота розуміння і ефективність.

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

Раннє виявлення помилок важливо ще й тому, що чим довше ми будемо відкладати їх пошук, тим сильніше виростуть витрати на виправлення. Це властивість, інтуїтивно зрозуміле всім програмістам-професіоналам, кількісно підтверджують широко відомі роботи Бема (Boehm). Залежність витрат на виправлення від часу відшукання помилок приведена на графіку, побудованому за даними ряду великих промислових проектів і проведених експериментів з невеликим керованим проектом:

Мал. 17.1. Порівняльні витрати на виправлення помилок ([Boehm 1981], публікується з дозволу)

Читабельність або Простота розуміння (readability) має свої переваги. У всіх прикладах цієї книги поява типу у сутності дає читачеві інформацію про її призначення. Читабельність вкрай важлива на етапі супроводу.

Статична типізація як і чому

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

Аргументи на користь динамічної типізації

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

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

Типізація: складові успіху

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

Наша система типів повністю заснована на понятті класу. Класами є навіть такі базові типи, як INTEGER. а отже, нам не потрібні особливі правила опису визначених типів. (В цьому наша нотація відрізняється від "гібридних" мов на зразок Object Pascal, Java і C ++, де система типів старих мов поєднується з об'єктної технологією, заснованої на класах.)

Розгорнуті типи дають нам більше гнучкості, допускаючи типи, чиї значення позначають об'єкти, поряд з типами, чиї значення позначають посилання.

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

Статична типізація як і чому

класичних типізованих мов, наприклад, Pascal і Ada, в яких оператор x: = y вимагає, щоб тип x і y був однаковим. Це правило дуже строго: воно забороняє використовувати суті, які можуть позначати об'єкти взаємопов'язаних типів (

SAVINGS_ACCOUNT і CHECKING_ACCOUNT). При спадкуванні ми вимагаємо лише сумісності типу y з типом x. наприклад, x має тип ACCOUNT. y - SAVINGS_ACCOUNT. і другий клас - спадкоємець першого.

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

Мал. 17.2. Множинне спадкування Універсальність необхідна, наприклад, для опису гнучких, але безпечних

У ряді випадків універсальність потрібно обмежити. що дозволяє використовувати операції, застосовні лише до сутностей родового типу. Якщо родовий клас SORTABLE_LIST підтримує сортування, він вимагає від сутностей типу G. де G - родовий параметр, наявності операції порівняння. Це досягається зв'язуванням з G класу, що задає родове обмеження, - COMPARABLE:

class SORTABLE_LIST [G -> COMPARABLE].

Будь фактичний родової параметр SORTABLE_LIST повинен бути нащадком класу COMPARABLE. має необхідний компонент.

Ще один обов'язковий механізм - спроба привласнення - організовує доступ до тих об'єктів, що типовий для ПО не керує. Якщо y - це об'єкт бази даних або об'єкт, отриманий через мережу, то оператор x? = Y присвоїть x значення y. якщо y має сумісний тип, або, якщо це не так, дасть x значення Void.

Твердження. пов'язані, як частина ідеї Проектування за Контрактом, з класами і їх компонентами в формі передумов, постусловіем і інваріантів класу, дають можливість описувати семантичні обмеження, які не охоплюються специфікацією типу. У таких мовах, як Pascal та Ada, є типи-діапазони, здатні обмежити значення сутності, наприклад, інтервалом від 10 до 20, проте, застосовуючи їх, вам не вдасться домогтися того, щоб значення i було негативним, завжди вдвічі перевищуючи j. На допомогу приходять інваріанти класів, покликані точно відображати вводяться обмеження, якими б складними вони не були.

Статична типізація як і чому

При розробці програмних систем на ділі необхідно ще одна властивість, властиве самому середовищі розробки - швидка, зростаюча (fast incremental) перекомпіляція. Коли ви пишете або вносите систему, хотілося б якомога швидше побачити ефект змін. При статичної типізації ви повинні дати компілятору час на перевірку типів. Традиційні підпрограми компіляції вимагають повторної трансляції всієї системи (і її збірки), і цей процес може бути болісно довгим, особливо з переходом до систем великого масштабу. Це явище стало аргументом на користь інтерпретує систем, таких як ранні середовища Lisp або Smalltalk, запускали систему практично без обробки, не виконуючи перевірку типів. Зараз цей аргумент забутий. Хороший сучасний компілятор визначає, як змінився код з моменту останньої компіляції, і обробляє лише знайдені зміни.

"Типізувати крихітка"?

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

Найпоширенішою лазівкою в статично типізованих мовах є наявність перетворень, що змінюють тип сутності. В C і похідних від нього мовами їх називають "приведенням типу" або кастингом (cast). Запис (OTHER_TYPE) x вказує на те, що значення x сприймається компілятором, як має тип OTHER_TYPE. при дотриманні деяких обмеженнях на можливі типи.

Подібні механізми обходять обмеження перевірки типів. Приведення широко поширене при програмуванні на мові C, включаючи діалект ANSI C. Навіть в мові C ++ приведення типів, хоча і не настільки часте, залишається звичним і, можливо, необхідною справою.

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

Далі будемо вважати, що система типів є суворою і не допускає приведення типу.

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

Типізація і зв'язування

Хоча як читач цієї книги ви напевно відрізните статичну типізацію від статичного зв'язування. є люди, яким подібне не під силу. Почасти це може бути

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

Як типізація, так і зв'язування мають справу з семантикою Базисної Конструкції x.f (arg). але відповідають на два різних питання:

Типізація і зв'язування

* Питання про типізації. коли ми повинні точно знати, що під час виконання з'явиться операція, відповідна f. застосовна до об'єкта, приєднаному до суті x (з параметром arg)?

* Питання про зв'язуванні. коли ми повинні знати, яку операцію ініціює даний

Типізація відповідає на питання про наявність як мінімум однієї операції, зв'язування відповідає за вибір потрібної.

В рамках об'єктного підходу:

* Проблема, що виникає при типізації, пов'язана з поліморфізмом. оскільки x під час виконання може позначати об'єкти кількох різних типів, ми повинні бути впевнені, що операція, яка представляє f. доступна в кожному з цих випадків;

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

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

* Smalltalk і інші ГО-мови містять засоби динамічного зв'язування і динамічної типізації. При цьому перевага віддається гнучкості на шкоду надійності мови.

* Окремі Необ'єктні мови підтримують динамічну типізацію і статичну зв'язування. Серед них - мови асемблера і ряд мов сценаріїв (scripting languages).

* Ідеї статичної типізації та динамічного зв'язування втілені в нотації, запропонованої в цій книзі.

Відзначимо своєрідність мови C ++, що підтримує статичну типізацію, хоча і не строгу зважаючи на наявність приведення типів, статичне зв'язування (за замовчуванням),

Причина вибору статичної типізації та динамічного зв'язування очевидна. Перше питання: "Коли ми будемо знати про існування компонентів?" - передбачає статичний відповідь: "Чим раніше, тим краще", що означає: під час компіляції. Друге питання: "Який з компонентів використовувати?" передбачає динамічний відповідь: "той, який потрібен", - відповідний динамічному типу об'єкта, що визначається під час виконання. Це єдино прийнятне рішення, якщо статична і динамічна зв'язування дає різні результати.

Наступний приклад ієрархії успадкування допоможе прояснити ці поняття:

Статична типізація як і чому

Мал. 17.3. Види літальних апаратів Розглянемо виклик:

Питання про типізації: коли переконатися, що тут буде компонент lower_landing_gear ( "випустити шасі"), який можна застосовувати до об'єкта (для COPTER його не буде зовсім) Питання про зв'язуванні: яку з декількох можливих версій вибрати.

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

Схожі статті