A lambda calculus for the HVM #97
August-Alm
started this conversation in
Ideas
Replies: 1 comment
-
That is amazing. I don't have much else to comment, but I'll be soon incorporating a HVM compatibility checker on Kind2. I have a different idea in mind, but if it doesn't work, that calculus looks like a much superior option than EAL. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Hi!
The HVM builds upon Lamping's abstract algorithm (the oracle-free part) and hence it cannot correctly reduce all lambda terms. The most succinct characterisation of which terms that are allowed is that "no clone may it's own clones". However, as far as I know there is no type theory that characterises this set of allowable terms, hence no type checker that can identify them; nor is there anything resembling a "borrow checker" that can do so. An early version of the Kind/Formality language used a lambda calculus based on Elementary Affine Logic (EAL) for this very reason, because EAL lambda terms belong to the class of terms that reduce correctly. However, the EAL conditions are much stronger than they need to be -- many more terms than those still reduce correctly. And EAL is, in my opinion at least, a bit weird semantically to program in.
I'd like to throw another ball into the air here. A computer scientist called Damiano Mazza has invented something he calls the Parsimonious Lambda Calculus. A simplified version of it, that I'd like to call the Very Parsimonious Lambda Calculus, is, in my mind, a much nicer calculus than EAL for practical programming, whilst still being reducible without an oracle. I don't think it captures all of the terms that reduce correctly, but more than EAL, and in a way that is easier for practical programming. Unlike EAL, it can also easily support recursion in the form of a Y-combinator.
The syntax extends the ordinary lambda calculus with a new form of abstraction lam! x. b and a modality !t. These combine in a new reduction rule, which, just like one would suspect, says that (lam! x. b !t) = b[t/x]. What makes the terms of this calculus reduce correctly without oracle is a kind of usage regiment:
Say that the "depth" of a variable x in a term t is the number of !:s inside of which it is nested.
In an ordinary lambda lam x.b, x occurs at most once in b and then only in depth zero.
In a lam! x.b, x can occur any number of times in depth zero, at most once in depth one, and not in higher depths.
So, for example, two = lam! s. lam z. (s (s z)) is a perfectly valid term. Recursion can be added in the form of a Y = (X !X) with
X = lam! x. lam! f. (f (x !x) f). (In the simple type system below, extended with polymorphism, (two !two) is not tapeable nor is X, but one can still add Y as a primitive of type forall a. !(a -> a) -> a. Note that recursion is restrained to be linear; one will have a "let rec f = bod" where bod can only contain one recursive call of f. Bit hard to write but opens up the possibility of automatically converting all recursions to tail-call / while loops.)
Semantically, one should think of !t as a constant stream of t:s. Or as a continuous emitter of t:s (compare with the ! in pi-calculus). One can pop/read as many t:s from it as one likes, without using the stream. Apart from that popping business everything is affine. (And applying ! means "pinning" a value as a constant stream.)
Moreover, these stratification rules can easily be incorporated in a type checker. Below is a small bidirectional implementation (in F#) of the "simply typed very parsimonious lambda calculus".
Beta Was this translation helpful? Give feedback.
All reactions