Фото: Олег Золото / РИА Новости
First, let’s consider a linked list:
,这一点在新收录的资料中也有详细论述
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.
DOSBox Debug is the hero of this story. It was the only tool in my arsenal to digest HELLO.EXE without problems, and its “heavy log” function was a game winner. In future I will probably upgrade to one of the more recent versions (“X” or “Staging”) but for this project the old vanilla DOSBox was enough.