Flanagan et al. 1993, many modern compilers
The language is pure, lazy, and has no loops. Every iteration is recursion, and recursion costs stack frames. Since Nix 2.20, the evaluator caps call depth at 10,000 (configurable via max-call-depth, but the default is what you'll hit). Before 2.20, the limit was whatever your OS allocated for the process stack: non-deterministic across machines, occasionally baffling to debug. Tail-call optimization would help. There's even a FIXME comment in ExprApp::eval() acknowledging it. But the evaluator's structure (a local variable that stays live across the recursive eval call) prevents the tail position from being optimized, and nobody has restructured the code. Tvix, the Rust-based evaluator, handles TCO in many cases. The reference C++ evaluator doesn't.
。关于这个话题,新收录的资料提供了深入分析
The fourth requirement (“Dynamic”) ensures that we only create dependencies when they are actually needed. This is easiest to see with conditional formulas, so something like IF(, slow_calculation(B1)). If the condition is true, this formula returns the value of (and therefore depends on) the cell B1. But if the condition is false, the formula returns nothing — and if B1 changes, this cell should not be updated. This is a dynamic dependency — the dependency only exists if is true.
ВсеОбществоПолитикаПроисшествияРегионыМосква69-я параллельМоя страна
Read full article