Чому компілятор Scala не може подати попередження про відповідність шаблону для незапечатаних класів / ознак?


10

Якщо я використовую ип запечатаний traitабо abstract classв Scala , а потім використовувати пошук по шаблоном, цікаво, це компілятор не знає під час компіляції для цієї конкретної patternmatch , які можливих реалізацій цієї ознаки / класу доступні? Отже, якщо це так, чи не може він подавати попередження відповідності шаблону, навіть якщо trait/ abstract classне запечатано, тому що він знає, які типи можна використовувати, перевіривши всі можливі залежності / імпорт?

Наприклад, якщо у мене є Option[A]і я збігаю шаблони тільки для, Some[A]але не для None, компілятор поскаржиться, оскільки Optionце запечатано.

Якщо компілятор не може це знати / вирішити, то чому він не може? І якщо компілятор (теоретично) може це зробити, то які причини цього не використовуються в Scala? Чи існують інші мови, які підтримують таку поведінку?


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

2
Якщо хтось може ввести новий підклас, відповідність шаблонів ніколи не може бути вичерпною. Наприклад, ви створюєте абстрактний клас Fooз підкласами A, Bі C, і всі ваші збіги шаблонів відповідають лише цим трьом. Ніщо не заважає мені додати новий підклас, Dякий підірве ваші відповідність шаблону.
Довал

@kdgregory Так, ви це отримали. Я додав приклад, щоб зробити це більш зрозумілим.
валентери

3
Перевірка всього імпорту недостатня для виявлення всіх можливих підкласів. Інший підклас може бути оголошений в окремому файлі класу, який пізніше завантажується під час виконання через java.lang.ClassLoader.
амон

Відповіді:


17

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

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

Це також одна з причин, чому JVM можуть настільки вигідно конкурувати з компіляторами C ++: компілятори C ++, як правило, є статичними компіляторами, тому вони взагалі не можуть з’ясувати, перевіряється чи ні метод, і, отже, не можуть його вбудувати. JVM OTOH, як правило, є динамічними компіляторами, їм не потрібно виконувати CHA, щоб з'ясувати, чи є метод замінений чи ні, вони можуть просто подивитися на Ієрархію класів під час виконання. І навіть якщо пізніше в процесі виконання програми з'явиться новий підклас, якого раніше не було, нічого великого, просто перекомпілювати цей фрагмент коду без вставки.

Примітка: все це стосується лише Scala. JVM не має поняття sealed, тому цілком можливо підкласи sealedкласів з іншої мови JVM, оскільки немає можливості донести це до іншої мови. sealedВластивість записується в ScalaSigанотації, але компілятори інших мов не брати ці анотацій до уваги, очевидно.


3

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

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


1
It can be done (at least for all classes known at compile time), it's just expensive.Але якщо програма не є на 100% самодостатньою (тобто це залежить від зовнішніх .jarфайлів), чи не змогли ви проникнути в новий підклас після компіляції через один із jars? Таким чином, компілятор може сказати вам "Ваші відповідність шаблону є вичерпними зараз, але це може змінитися, якщо зміниться будь-яка з ваших залежностей", що досить марно, оскільки з точки зору виникнення зовнішніх залежностей можна мати можливість оновити їх без перекомпіляції!
Довал

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

@Doval Тоді вам слід скористатись якоюсь формою делегування та дозволити класу, який викликається, вирішити, що робити та інвертувати контроль. Це те, для чого призначено ООП. Якщо ви цього не хочете, у вас є ваш поточний випуск.
Санки

@ArtB Я не бачу, що делегування чи інверсія контролю мають відношення до цього.
Довал

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