Для тих із вас, хто має щастя не працювати мовою з динамічним розмахом, дозвольте мені трохи оновити, як це працює. Уявіть псевдомову, яку називають "RUBELLA", яка поводиться так:
function foo() {
print(x); // not defined locally => uses whatever value `x` has in the calling context
y = "tetanus";
}
function bar() {
x = "measles";
foo();
print(y); // not defined locally, but set by the call to `foo()`
}
bar(); // prints "measles" followed by "tetanus"
Тобто, змінні розповсюджуються вільно та вниз стеком викликів - усі змінні, визначені в foo, видно (і мутації) його виклику bar, і зворотне також є істинним. Це має серйозні наслідки для відновлення коду. Уявіть, що у вас є такий код:
function a() { // defined in file A
x = "qux";
b();
}
function b() { // defined in file B
c();
}
function c() { // defined in file C
print(x);
}
Тепер дзвінки до a()друку надрукуються qux. Але потім, коли-небудь, ти вирішиш, що потрібно bтрохи змінити . Ви не знаєте всіх викликів викликів (деякі з яких насправді можуть бути поза вашою базою кодів), але це повинно бути добре - ваші зміни будуть повністю внутрішніми b, правда? Отже, ви перепишете його так:
function b() {
x = "oops";
c();
}
І ви можете подумати, що ви нічого не змінили, оскільки тільки що визначили локальну змінну. Але насправді ти зламав a! Тепер aдрукує, oopsа не qux.
Якщо повернути це з сфери псевдомов, саме так поводиться MUMPS, хоча і з різним синтаксисом.
Сучасні («сучасні») версії MUMPS включають так званий NEWоператор, який дозволяє запобігти просоченню змінних від виклику до абонента. Так, у першому прикладі вище, якби ми це зробили NEW y = "tetanus"в foo(), тоді print(y)в bar()нічого б не надрукували (у MUMPS всі імена вказують на порожню рядок, якщо явно не встановлено щось інше). Але ніщо не може перешкоджати просоченню змінних від абонента до виклику: якщо ми function p() { NEW x = 3; q(); print(x); }, наскільки ми знаємо, q()могли б мутувати x, незважаючи на те, що явно не отримували xпараметр. Це все ще погана ситуація, але не така погана, як це, мабуть, раніше.
Маючи на увазі ці небезпеки, як можна безпечно перетворювати код на MUMPS або будь-яку іншу мову з динамічним визначенням масштабу?
Існує кілька очевидних добрих практик для полегшення рефакторингу, наприклад, ніколи не використовуйте змінних у функції, відмінній від тих, яку ви ініціалізуєте ( NEW) самі або передаєте як явний параметр, і явно документуйте будь-які параметри, які неявно передаються від абонентів функції. Але в десятиліттях, ~ 10 8 -LOC-коди, таких розкошів, яких часто немає.
І, звичайно, по суті всі добрі практики рефакторингу в мовах з лексичною сферою застосовуються також у мовах з динамічною сферою - пишіть тести тощо. Питання, таким чином, таке: як ми пом’якшуємо ризики, специфічно пов’язані з підвищеною крихкістю динамічно-охопленого коду під час рефакторингу?
(Зауважте, що в той час, як код навігації та рефактора написаний динамічною мовою? Має подібний заголовок до цього питання, він повністю не пов'язаний.)