При цьому letвсі вирази, що ініціалізують змінну, бачать абсолютно те саме лексичне середовище: те, що оточує let. Якщо випадково ці вирази фіксують лексичні закриття, усі вони можуть мати спільний об’єкт середовища.
При цьому let*кожен ініціалізуючий вираз знаходиться в іншому середовищі. Для кожного наступного виразу середовище повинно бути розширене, щоб створити нове. Принаймні в абстрактній семантиці, якщо закриття фіксується, вони мають різні об'єкти навколишнього середовища.
let*Повинно бути добре оптимізовано , щоб згорнути непотрібні розширення середовища для того , щоб підходять в якості заміни для повсякденної let. Повинен бути компілятор, який працює, форми якого отримують доступ до чого, а потім перетворює всі незалежні у більші комбіновані let.
(Це справедливо, навіть якщо let*це просто макрооператор, який випромінює каскадні letформи; оптимізація проводиться на цих каскадних lets).
Ви не можете реалізувати let*як єдиний наївний елемент letіз прихованими присвоєннями змінних для ініціалізації, оскільки буде виявлено відсутність належного масштабування:
(let* ((a (+ 2 b))
(b (+ 3 a)))
forms)
Якщо це перетвориться на
(let (a b)
(setf a (+ 2 b)
b (+ 3 a))
forms)
в цьому випадку це не спрацює; внутрішнє bзатінює зовнішнє, bтому в підсумку ми додаємо 2 до nil. Таке перетворення можна здійснити, якщо ми перейменуємо всі ці змінні. Потім довкілля добре вирівнюється
(let (#:g01 #:g02)
(setf #:g01 (+ 2 b)
#:g02 (+ 3 #:g01))
alpha-renamed-forms)
Для цього нам потрібно розглянути підтримку налагодження; якщо програміст вступає в цей лексичний обсяг за допомогою налагоджувача, чи хочемо ми, щоб вони мали справу із #:g01замість a.
Отже, в основному, let*це складна конструкція, яка повинна бути оптимізована добре для роботи, а також letу випадках, коли вона може зменшитись до let.
Це саме по собі не виправдовує користь letбільш let*. Припустимо, у нас хороший компілятор; чому не використовувати let*весь час?
Як загальний принцип, ми повинні віддавати перевагу конструкціям вищого рівня, які роблять нас продуктивними та зменшують помилки, над конструкціями нижчого рівня, схильними до помилок, і покладаємося якомога більше на хороші реалізації конструкцій вищого рівня, так що нам рідко доводиться жертвувати їх використання заради продуктивності. Ось чому ми працюємо такою мовою, як Lisp.
Це міркування не найкраще стосується letversus let*, оскільки let*це явно не абстракція вищого рівня щодо let. Вони приблизно на "рівному рівні". За допомогою let*ви можете ввести помилку, яка вирішується простим переходом на let. І навпаки . let*насправді є лише м’яким синтаксичним цукром для візуально руйнуючогося letгніздування, а не значною новою абстракцією.