Es ist ein weit verbreitetes Missverständnis, dass wir let
Ausdrücke in Anwendungen übersetzen können. Der Unterschied zwischen let x : t := b in v
und (fun x : t => v) b
besteht darin, dass wir im let
Ausdruck während der Typprüfung v
wissen, dass dies x
gleich b
ist, in der Anwendung jedoch nicht (der Unterausdruck fun x : t => v
muss für sich genommen Sinn machen).
Hier ist ein Beispiel:
(* 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).
Ihr Vorschlag, die Bewerbung zu (fun x : t => v) b
einem Sonderfall zu machen, funktioniert nicht wirklich. Lassen Sie uns genauer darüber nachdenken.
Wie würden Sie zum Beispiel damit umgehen und das obige Beispiel fortsetzen?
Definition a := (fun (n : nat) => cons n 42 nil).
Check a 0.
Vermutlich funktioniert dies nicht, weil a
es nicht eingegeben werden kann, aber wenn wir seine Definition entfalten, erhalten wir einen gut typisierten Ausdruck. Glauben Sie, dass die Benutzer uns lieben oder für unsere Designentscheidung hassen werden?
e₁ e₂
e₁
λ
Sie würden auch den Grundsatz brechen, der besagt, dass jeder Unterausdruck eines gut typisierten Ausdrucks gut typisiert ist. Das ist genauso sinnvoll wie die Einführung null
in Java.
let
Ausdrücke benötigt werden, aber es gibt a) keinen Grund,let
Ausdrücke zu vermeiden , und sie sind auch praktisch, und b) das Hinzufügen von Hacks zu Ihrer Kernsprache ist keine gute Idee.