Jest powszechnym błędnym przekonaniem, że możemy tłumaczyć let
-wyrażenia na aplikacje. Różnica między let x : t := b in v
i (fun x : t => v) b
polega na tym, że w let
-wyrażeniu, podczas sprawdzania typu v
wiemy, że x
jest on równy b
, ale w aplikacji tak nie jest (podwyrażenie fun x : t => v
musi mieć sens samo w sobie).
Oto przykład:
(* 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).
Twoja sugestia, aby uczynić aplikację (fun x : t => v) b
specjalnym przypadkiem, tak naprawdę nie działa. Zastanówmy się nad tym ostrożniej.
Na przykład, jak sobie z tym poradzisz, kontynuując powyższy przykład?
Definition a := (fun (n : nat) => cons n 42 nil).
Check a 0.
Prawdopodobnie to nie zadziała, ponieważ a
nie można go wpisać, ale jeśli rozwiniemy jego definicję, otrzymamy dobrze napisane wyrażenie. Czy uważasz, że użytkownicy będą nas kochać, czy nienawidzą nas za naszą decyzję dotyczącą projektu?
Musisz dokładnie przemyśleć, co to znaczy mieć „specjalny przypadek”. Jeśli mam aplikację e₁ e₂
, czy powinienem ją znormalizować, e₁
zanim zdecyduję, czy jest to abstrakcja ? Jeśli tak, oznacza to, że będę normalizować wyrażenia źle wpisane, a te mogą się cyklicznie zmieniać. Jeśli nie, użyteczność twojej propozycji wydaje się wątpliwa.λ
Zerwalibyście także podstawowe twierdzenie, które mówi, że każde podwyrażenie dobrze napisanego wyrażenia jest dobrze napisane. To tak rozsądne, jak wprowadzenie null
do Javy.
let
wyrażeń, ale nie ma a) powodu, aby unikaćlet
wyrażeń i są one również wygodne, oraz b) dodanie hacków do twojego podstawowego języka nie jest świetnym pomysłem.