4 ms·
Two examples that satisfy these criteria are Dhall [1] and Cue [2], but in one sense they are not interesting examples: Dhall is a total language on purpose, it
by ruuda 2y ago
Two examples that satisfy these criteria are Dhall [1] and Cue [2], but in one sense they are not interesting examples: Dhall is a total language on purpose, it does go the “not Turing-complete” route, and Cue has no functions so there is nothing to recurse.
I would say that RCL [3] satisfies the criteria. It’s deterministic and pure, has metered execution, and it sandboxes filesystem access. It can read files when allowed by the sandbox policy, but in my view that makes those files part of the source code, it behaves the same as imports.
For RCL I did not want to go the “not Turing-complete” route for exactly the reason the author mentions: knowing that a program enventually terminates is not a useful property in practice. And conversely, it is possible to write very complex programs in total languages like Agda, non-Turing-completeness is no guarantee for simple programs/configurations. All loops in RCL are bounded, but it has functions, so it has recursion. It does not have tail calls, so at first I added a recursion depth limit (to prevent overflowing the native stack), but then the fuzzer discovered a function that runs in constant stack space, yet it hangs. I still don't fully understand how it works:
let f = g => g(g(h => k => g(g(h))));
f(f)
Anyway, it is not a problem in practice that this kind of pathological function can be expressed, I just put a limit on the number of execution steps (a “gas limit”, or what the author calls “metered execution”). For keeping code simple, I think the fact that the built-in looping constructs are bounded, and recursion is awkward, are a good nudge, but in the end the most valuable tool is code review and applying good judgment.
[1]: https://dhall-lang.org/
[2]: https://cuelang.org/
[3]: https://rcl-lang.org/
- 4ad 2y ago> Cue has no functions so there is nothing to recurse. CUE developer here. This is wrong. CUE doesn't have functions, but it does have abstraction and beta-reduction, just like lambda calculus. Types can refer to themselves. There is also mutual recursion between types. We have a termination checker than ensures CUE programs are total (although it works by very different principles compared to other total languages). If you disable the CUE termination checker you get a a Turing complete language. If you leave it alone you get a primitive recursive language. Here is CUE implementing an arbitrary number of steps of rule 110 cellular automaton[1], which is Turing complete: https://cuelang.org/play/?id=Ityqia88Mvq#w=function&i=cue&f=export&o=cue https://cuelang.org/play/?id=Ityqia88Mvq#w=function&i=cue&f=... [0] https://en.wikipedia.org/wiki/Rule_110 https://en.wikipedia.org/wiki/Rule_110
- ruuda 2y agoThanks for pointing that out, I didn’t know types in Cue could be self-referential.
- yorwba 2y ago> a function that runs in constant stack space, yet it hangs I don't think it actually runs in constant stack space, but it takes an exponential number of execution steps to get to any given stack depth, as the number of function arguments that need to be unraveled doubles with every invocation of f.
- ruuda 2y agoThat’s interesting!