Это распространенное заблуждение, что мы можем переводить let
выражения-приложения. Разница между let x : t := b in v
и (fun x : t => v) b
заключается в том let
, что в выражении -ex во время проверки типа 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₁
прежде чем я решу, является ли это -abstraction? Если да, это означает, что я буду нормализовать плохо напечатанные выражения, и они могут циклически повторяться. Если нет, удобство использования вашего предложения кажется сомнительным.λ
Вы также нарушите фундаментальную теорему, которая гласит, что каждое подвыражение хорошо типизированного выражения хорошо типизировано. Это так же разумно, как знакомство null
с Java.
let
выражениях, но а) нет причин избегатьlet
выражений, и они также удобны, и б) добавление хаков в ваш основной язык не очень хорошая идея.