Чи є реальна перевага динамічних мов? [зачинено]


29

По-перше, я хочу сказати, що Java є єдиною мовою, яку я коли-небудь використовував, тому, будь ласка, вибачте моє незнання з цього приводу.

Динамічно набрані мови дозволяють вводити будь-яке значення в будь-яку змінну. Так, наприклад, ви можете написати таку функцію (psuedocode):

void makeItBark(dog){
    dog.bark();
}

І ви можете передавати всередині нього будь-яку цінність. Поки значення має bark()метод, код буде працювати. Інакше викидається виняток із виконання або щось подібне. (Будь ласка, виправте мене, якщо я помиляюся з цього приводу).

Здається, це дає вам гнучкість.

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

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

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

Я щось пропускаю? Що я насправді здобуваю, використовуючи динамічно набрану мову?


6
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof")). Цей аргумент навіть не є класом , це анонімний назви кортеж. Введення качки ("якщо вона б'ється як ...") дозволяє вам робити спеціальні інтерфейси з по суті нульовими обмеженнями та без синтаксичних накладних витрат. Ви можете зробити це такою мовою, як Java, але в кінцевому підсумку ви отримаєте безліч брудних роздумів. Якщо для функції Java потрібен ArrayList, і ви хочете надати їй інший тип колекції, ви SOL. У пітоні це навіть не підходить.
Phoshi

2
Таке запитання було задано раніше: тут , тут і тут . Зокрема, перший приклад, здається, відповідає на ваше запитання. Може, ви можете перефразовувати своє, щоб зробити його чіткішим?
logc

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

2
@Phoshi Аргумент в Python все ще повинен бути певного типу - наприклад, це не може бути число. Якщо у вас є власна спеціальна реалізація об'єктів, яка отримує її членів за допомогою певної спеціальної getMemberфункції, makeItBarkвибухне, тому що ви викликали dog.barkзамість dog.getMember("bark"). Що змушує роботу коду полягає в тому, що всі неявно погоджуються використовувати тип власного об'єкта Python.
Довал

2
@Phoshi Just because I wrote makeItBark with my own types in mind doesn't mean you can't use yours, wheras in a static language it probably /does/ mean that.Як зазначено в моїй обороні, це не так взагалі . Це стосується Java та C #, але ці мови мають покалічені системи та модульні системи, тому вони не є репрезентативними для того, що може робити статичне введення тексту. Я можу написати ідеально загальну makeItBarkдля кількох мов, що мають статичний тип, навіть нефункціональні, такі як C ++ або D.
Doval

Відповіді:


35

Мови, що динамічно вводяться, є однотипними

Порівнюючи системи типів , немає переваги в динамічному наборі тексту. Динамічне введення тексту є особливим випадком статичного набору тексту - це статично типова мова, де кожна змінна має один і той же тип. Те ж саме можна досягти і в 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вимогам. Це може здатися схожим на interfaceJava або C #, але це має одну велику перевагу - типи не повинні вказувати наперед, для яких типів класів вони задовольняються. Я можу сказати, що тип Duckє Barkableв будь-який час, навіть якщо Duckце сторонній тип, який я не писав. Насправді, неважливо, що автор Duckне написав barkфункції - я можу надати її після факту, коли я розмовляю мовою, яка Duckзадовольняє Barkable:

instance Barkable Duck where
    bark d = quack (punch (d))

makeItBark (aDuck)

Це говорить про те, що Ducks може брахати, а їх кора функція реалізується шляхом штампування качки перед тим, як зробити її кряканням. Якщо це не виходить, ми можемо закликати 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, це врахування нерівності потрібно враховувати. Існує різниця між порівнянням мов із усіма батареями , мовними ядрами та типами систем .


2
Ви забули приписати джерело першому абзацу - existentialtype.wordpress.com/2011/03/19/…

2
@Matt Re: 1, я не вважав, що це не важливо; Я звернувся до нього під лаконічністю. Re: 2, хоча я ніколи цього прямо не сказав, маючи на увазі, "добре" я маю на увазі "має ретельний висновок про тип" і "має модульну систему, яка дозволяє вам відповідати код для введення підписів після факту ", а не наперед, як Java / C # 'інтерфейси. Знову 3, тягар доказування лежить на тому, щоб ви мені пояснили, як надані дві мови з еквівалентним синтаксисом та функціями, одна динамічно набрана та інша з повним висновком типу, ви не зможете написати код однакової довжини в обох .
Doval

3
@MattFenwick Я вже це виправдав - з огляду на дві мови з однаковими функціями, одну динамічно набрану та іншу статично типовою, головна відмінність між ними полягатиме в наявності анотацій типів, а висновок типу забирає це. Будь-які інші відмінності в синтаксисі є поверхневими, і будь-які відмінності в особливостях перетворюють порівняння на яблука проти апельсинів. Це на вас, щоб показати, як ця логіка неправильна.
Довал

1
Ви повинні подивитися на Бу. Він статично набраний з висновком типу та має макроси, які дозволяють розширити синтаксис мови.
Мейсон Уілер

1
@Doval: Правда. BTW, лямбда-позначення не використовуються виключно у функціональному програмуванні: наскільки я знаю, у Smalltalk є анонімні блоки, а у Smalltalk настільки об'єктно-орієнтовані, наскільки це можливо. Отже, часто рішення полягає в тому, щоб пройти навколо анонімного блоку коду з деякими параметрами, незалежно від того, чи це анонімна функція або анонімний об'єкт точно одним анонімним методом. Я думаю, що ці дві конструкції виражають по суті одну і ту ж ідею з двох різних точок зору (функціональної та об'єктно-орієнтованої).
Джорджіо

11

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

Ось мій погляд на це: Суть мов високого рівня полягає в обмеженні того, що програміст може зробити з комп'ютером. Це дивує багатьох людей, оскільки вони вважають, що мета - надати користувачам більше влади та досягти більше . Але оскільки все, що ви пишете в Prolog, C ++ або List, врешті-решт виконується як машинний код, фактично неможливо дати програмісту більше енергії, ніж мова монтажу вже передбачена.

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

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

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

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

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


3
Дякую за відповідь. Ви сказали, що деякі люди не люблять сказати укладачем, що робити. [..] Вони віддають перевагу дослідницькому стилю програмування, де ви пишете фактичний бізнес-код, не маючи плану, який би точно розказував, які типи та аргументи використовувати де. ' Це я не розумію: програмування не схоже на музичну імпровізацію. У музиці, якщо ви потрапили в неправильну ноту, це може звучати круто. У програмуванні, якщо ви передасте щось у функцію, яка там не повинна бути, ви, швидше за все, отримаєте неприємні помилки. (продовження в наступному коментарі).
Авів Кон

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

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

1
Люди часто говорять про дуже різні типи проектів (особливо щодо розміру). Логіка бізнесу для веб-сайту буде дуже простою порівняно з повною системою ERP. Існує менше ризику, що ви помилитесь, а перевага можливості простого використання якогось коду є більш актуальною. Скажімо, у мене є якийсь код, який генерує Pdf (або якийсь HTML) із структури даних. Тепер у мене інше джерело даних (спочатку був JSON з якогось REST API, зараз це імпортер Excel). Такою мовою, як Ruby, можна «легко» імітувати першу структуру, «змусити її бракувати» та повторно використовувати Pdf-код.
thorsten müller

@Prog: Справжня перевага динамічних мов полягає в тому, що мова йде про опис речей, що дуже важко для системи статичного типу. Наприклад, функція в python може бути посиланням на функцію, лямбда, об'єктом функції або бог знає, що і все буде працювати однаково. Ви можете побудувати об’єкт, який обгортає інший об’єкт і автоматично розсилає методи з нульовою синтаксичною накладними, і кожна функція, по суті, магічно має параметризовані типи. Динамічні мови є дивовижними для того, щоб швидко виконати роботу.
Фоши

5

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

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


2
Не схильні люди врешті-решт впроваджувати базову систему статичного типу за допомогою своїх тестових одиниць (при орієнтації на хороше покриття тесту)?
День

Також що ви маєте на увазі під "сполученням" тут? Як би це виявилося, наприклад, в архітектурі мікропослуг?
День

@День 1) Хороше запитання, проте я відчуваю, що це поза рамками ОП та моєї відповіді. 2) я маю на увазі сполучення в цьому сенсі ; коротко, системи різних типів накладають різні (несумісні) обмеження на код, написаний цією мовою. Вибачте, я не можу відповісти на останнє запитання - я не розумію, у чому полягає особливість мікро-сервісів у цьому плані.

2
@Den: Дуже хороший момент: я часто спостерігаю, що одиничні тести я записую в помилки покриття Python, які вловлював би компілятор статично набраною мовою.
Джорджіо

@MattFenwick: Ви писали, що це перевага, що "... для динамічної мови систему статичного типу не потрібно проектувати, впроваджувати, перевіряти та підтримувати". і Ден зауважив, що вам часто доводиться проектувати і тестувати свої типи безпосередньо в коді. Таким чином, зусилля не знімаються, а переходять від мовної розробки до коду програми.
Джорджіо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.