Поширена помилкова думка, що ми можемо переводити 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
виразів, і вони також зручні; б) додавання хаків до вашої основної мови - не чудова ідея.