Поширена помилкова думка, що ми можемо переводити let-вираження у додатки. Різниця між let x : t := b in vі (fun x : t => v) bполягає в тому, що в let-expression під час перевірки типів vми знаємо, що xдорівнює b, але в додатку ми цього не робимо (субекспресія fun x : t => vмає мати сенс самостійно).
Ось приклад:
(* Dependent type of vectors. *)
Inductive Vector {A : Type} : nat -> Type :=
| nil : Vector 0
| cons : forall n, A -> Vector n -> Vector (S n).
(* This works. *)
Check (let n := 0 in cons n 42 nil).
(* This fails. *)
Check ((fun (n : nat) => cons n 42 nil) 0).
Ваша пропозиція зробити заявку (fun x : t => v) bособливою справою насправді не працює. Давайте подумаємо про це ретельніше.
Наприклад, як би ви з цим впоралися, продовжуючи вищевказаний приклад?
Definition a := (fun (n : nat) => cons n 42 nil).
Check a 0.
Імовірно, це не спрацює, тому що aне може бути введено, але якщо ми розгорнемо його визначення, ми отримаємо чітко набраний вираз. Як ви думаєте, користувачі нас люблять або ненавидять нас за наше дизайнерське рішення?
Вам потрібно добре подумати, що означає мати «особливий випадок». Якщо у мене є додаток e₁ e₂, чи варто його нормалізувати, e₁перш ніж вирішити, чи це абстракція? Якщо так, це означає, що я буду нормалізувати неправильні вводити вирази, і вони можуть циркулювати. Якщо ні, зручність використання вашої пропозиції здається сумнівною.λ
Ви також порушите основоположну теорему, яка говорить про те, що кожний підвираз добре введеного виразу є добре набраним. Це так само розумно, як впровадження nullв Java.
letвиразів, але немає а) причин уникатиletвиразів, і вони також зручні; б) додавання хаків до вашої основної мови - не чудова ідея.