Так чи погана статична типізація, записки програміста

Останнім часом все більше людей вибирають мови програмування з динамічною типізацією. Прихильники динамічної типізації стверджують, що на вивчення Erlang'а потрібно два тижні, після чого можна разу почати писати бойової код. Що все одно інтернети динамічно типізовані, що помилки типізації швидко знаходяться і легко усуваються, а справжню проблему представляють складні логічні помилки, де статична типізація все одно не допомогла б. Що статична типізація - це повільно і нудно, а на Clojure можна легко написати свій Mortal Kombat за два вечори. Давайте ж з'ясуємо, чому насправді ви не повинні хотіти динамічної типізації.

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

Навіть в мовах з динамічною типізацією використовувати одну змінну для зберігання значень різних типів, наприклад, рядки або числа, вважається code smell. В результаті, ви все одно виводите типи в розумі. Тільки, на відміну від компілятора, іноді ви помиляєтеся. У деяких динамічно типізованих мовах є опціональна перевірка типів на етапі компіляції. Але, наприклад, у випадку з Erlang (1) вона працює в рази повільніше перевірки типів в Haskell, (2) при її використанні вам фактично доводиться виводити типи вручну або в напівавтоматичному режимі за допомогою TypEr, (3) відповідна програма, Dialyzer, вельми глюкавий. Тож не дивно, що на практиці ерлангісти часто забивають на Dialyzer і виводять типи в розумі. Питається, раз ви все одно використовуєте типи, чому б не доручити компілятору виводити і перевіряти їх?

Доповнення: Незалежні джерела повідомляють. що схожі проблеми мають місце бути в світі Clojure.

Деякі програмісти абсолютно незаслужено дорікають статично типізовані мови в багатослівності або недостатню гнучкість. Насправді, ніщо не заважає, наприклад, писати на Haskell, немов це Erlang з прикрученим Dialyzer. Тобто, використовувати списки, кортежі, рядки і числа, не вводячи ніяких додаткових типів. Якщо ж вам раптом дуже захочеться оголосити список, в якому зберігаються як рядки, так і числа, то можна просто ввести тип data StrOrInt = S String | I Int і потім оголосити список StrOrInt'ов. Або взяти готовий тип Either. Або ж можна скористатися екзистенційними типами.

Багато хто помилково вважає, що статична типізація захищає тільки від обмеженого класу помилок, наприклад, що ви не складете рядок з числом. Насправді, з її допомогою можна зробити набагато більше. Наприклад, в веб-фреймворку Yesod за допомогою типів, крім іншого, перевіряється, що програма не посилатиметься на неіснуючий URL. Повсюдно в Haskell за допомогою типів здійснюється контроль над побічними ефектами. Крім іншого, це позитивним чином позначається на продуктивності додатка. Наприклад, якщо потрібно виконати розрахунки над якимись даними, ви один раз отримаєте їх в «брудному» коді, а потім передасте в чисту функцію, замість того, щоб постійно отримувати одні і ті ж дані в різних функціях. У Haskell завдяки типам гарантується, що при роботі зі STM. перебуваючи всередині транзакції, ви не спробуєте передати що-небудь по мережі, а при роботі з Template Haskell - що ви не вклеїли шаблон туди, де йому не місце.

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

До речі, про тести. Досвід показує, що переконати колег у необхідності написання тестів досить важко. Що вже гріха таїти, навіть самого себе в цьому переконати непросто. Писатимуть тести нудно. На їх підтримку потрібно витрачати час. В реальних умовах вимоги до додатка постійно змінюються. А це означає постійне переписування тестів. Значить, або програмісти працюють повільніше, або потрібно наймати додаткових людей для підтримки тестів. При тестуванні великого і складного додатка тести також перетворюються в велике і складне програму, що не менше тестованого потребує оптимізації, а також в пошуку і усунення помилок. На практиці неможливо покрити тестами 100% коду, а як автоматизувати тестування таких додатків, як компілятори, тривимірні ігри або пошукові системи, взагалі незрозуміло. Я вже не кажу про те, що на тести часто просто забивають.

Нарешті, як недолік мов зі статичної типізацією нерідко називають їх складність у вивченні. По-перше, у мене особисто з цього приводу є сильні сумніви. Я б не сказав, що Scala або OCaml істотно складніше у вивченні, ніж Perl або Erlang. Та й Haskell, насправді, не дуже складний, просто вивчати його потрібно повільно і вдумливо, а не наскоком. По-друге, навіть якщо це і так, на практиці у вас навряд чи виникне сильне бажання писати проект на Erlang з програмістом, який тиждень тому прочитав книжку Чезаріні і Томпсона. Швидше за все, вам захочеться знайти людину, яка принаймні протягом півроку писав на Erlang якісь проектики для себе. І по-третє, вже краще витратити N місяців на вивчення Haskell, після чого почати писати на ньому цілком придатний код, ніж за пару тижнів освоїти будь-який інший мову і зрозуміти, як насправді потрібно на ньому писати, тільки через кілька років.

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

Схожі статті