Мови, що динамічно вводяться, є однотипними
Порівнюючи системи типів , немає переваги в динамічному наборі тексту. Динамічне введення тексту є особливим випадком статичного набору тексту - це статично типова мова, де кожна змінна має один і той же тип. Те ж саме можна досягти і в Java (мінус стислість), зробивши кожну змінну типу Object
, а типи "об'єктних" значень типу Map<String, Object>
:
void makeItBark(Object dog) {
Map<String, Object> dogMap = (Map<String, Object>) dog;
Runnable bark = (Runnable) dogMap.get("bark");
bark.run();
}
Таким чином, навіть не задумуючись, ви можете досягти такого ж ефекту майже в будь-якій мові, що має статичний характер, а також синтаксичну зручність. Ви не отримуєте додаткової виразної сили; навпаки, ви маєте меншу виразність, оскільки мовою, що динамічно набирається, вам відмовлено у можливості обмеження змінних певними типами.
Складання кори качки мовою статичного типу
Крім того, хороша статична мова дозволить вам написати код, який працює з будь-яким типом, який має bark
операцію. У Haskell це клас типу:
class Barkable a where
bark :: a -> unit
Це виражає обмеження, що для певного типу, a
який вважається Баркабельним, повинна існувати bark
функція, яка приймає значення цього типу і нічого не повертає.
Потім можна записати загальні функції з точки зору Barkable
обмеження:
makeItBark :: Barkable a => a -> unit
makeItBark barker = bark (barker)
Це говорить про те, що makeItBark
буде працювати для будь-якого типу, що відповідає Barkable
вимогам. Це може здатися схожим на interface
Java або C #, але це має одну велику перевагу - типи не повинні вказувати наперед, для яких типів класів вони задовольняються. Я можу сказати, що тип Duck
є Barkable
в будь-який час, навіть якщо Duck
це сторонній тип, який я не писав. Насправді, неважливо, що автор Duck
не написав bark
функції - я можу надати її після факту, коли я розмовляю мовою, яка Duck
задовольняє Barkable
:
instance Barkable Duck where
bark d = quack (punch (d))
makeItBark (aDuck)
Це говорить про те, що Duck
s може брахати, а їх кора функція реалізується шляхом штампування качки перед тим, як зробити її кряканням. Якщо це не виходить, ми можемо закликати makeItBark
качок.
Standard ML
і OCaml
ще більш гнучкими в тому, що ви можете задовольнити один і той же тип класу більш ніж одним способом. Цими мовами я можу сказати, що цілі числа можна замовити за допомогою звичайного впорядкування, а потім розвернутися і сказати, що вони також можуть бути впорядковані на поділ (наприклад, 10 > 5
тому що 10 ділиться на 5). У Haskell ви можете інстанціювати тип типу лише один раз. (Це дозволяє Haskell автоматично знати, що нормально дзвонити bark
на качку; в SML або OCaml ви повинні мати чітку інформацію про те, яку bark
функцію ви хочете, оскільки їх може бути більше.)
Лаконічність
Звичайно, є синтаксичні відмінності. Представлений вами код Python набагато більш стислий, ніж аналог, який я написав. На практиці ця стислість є важливою частиною привабливості динамічно набраних мов. Але висновок типу дозволяє писати код, який настільки ж стислий на мовах статичного типу, позбавляючи вас від необхідності чітко писати типи кожної змінної. Статично введена мова також може забезпечити нативну підтримку динамічного набору тексту, видаляючи багатослівність усіх маніпуляцій із литтям та картою (наприклад, C # 's dynamic
).
Правильні, але неправильно набрані програми
Якщо чесно, статичне введення обов'язково виключає деякі технічно правильні програми, навіть якщо перевіряючий тип не може його перевірити. Наприклад:
if this_variable_is_always_true:
return "some string"
else:
return 6
Більшість мов, які мають статичний тип, відхилять це if
твердження, навіть якщо інша гілка ніколи не зустрінеться. На практиці, здається, ніхто не використовує цей тип коду - все, що занадто розумно для перевірки типів, ймовірно, змусить майбутніх утримувачів вашого коду проклинати вас і ваших близьких. Насправді, хтось успішно перевів 4 проекти з відкритим кодом Python на Haskell, а це означає, що вони не робили нічого, що не могло б скласти гарна статична мова. Більше того, компілятор знайшов пару помилок, пов’язаних з типом, які одиничні тести не знайшли.
Найсильніший аргумент, який я бачив під час динамічного набору тексту, - це макроси Ліспа, оскільки вони дозволяють довільно розширити синтаксис мови. Тим не менш, Typed Racket - це діалект з лісовим типом, який має макроси, тому здається, що статичне введення тексту та макроси не є взаємовиключними, хоча, можливо, важче реалізувати одночасно.
Яблука та апельсини
Нарешті, не забувайте, що в мовах є великі розбіжності, ніж просто їх тип типів. До Java 8 виконання будь- якого функціонального програмування на Java було практично неможливим; для простої лямбда потрібні 4 рядки анонімного коду класу. Java також не має підтримки для літератур колекції (наприклад [1, 2, 3]
). Також можуть бути різниці у якості та доступності інструментарію (IDE, налагоджувачі), бібліотек та підтримки громади. Коли хтось стверджував, що в Python або Ruby більш продуктивним, ніж на Java, це врахування нерівності потрібно враховувати. Існує різниця між порівнянням мов із усіма батареями , мовними ядрами та типами систем .
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof"))
. Цей аргумент навіть не є класом , це анонімний назви кортеж. Введення качки ("якщо вона б'ється як ...") дозволяє вам робити спеціальні інтерфейси з по суті нульовими обмеженнями та без синтаксичних накладних витрат. Ви можете зробити це такою мовою, як Java, але в кінцевому підсумку ви отримаєте безліч брудних роздумів. Якщо для функції Java потрібен ArrayList, і ви хочете надати їй інший тип колекції, ви SOL. У пітоні це навіть не підходить.