-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Loops #54
Comments
There's a major problem with this setup, though. Currently our strategy is to let the user write numbers and think of them as numbers, but lift those numbers into a finite field as well as all operations over them. This works fine as long as we don't need to "downcast" elements of the field into integers, which is what we do to compute comparisons, for example. This can be dangerous, for example, In the same way it doesn't make much sense to downcast elements of the field to get integer bounds. No user expects Perhaps we want a separate type of integers that, say, all disappear during compilation. That could work for loop bounds at least (I doubt it's enough to get working comparisons, but I haven't spent any time investigating this issue). |
Maybe instead we should go for primitive recursion? Although I suspect that primitive recursion will be more difficult to use than loops. What are loops actually used for in such language? Are they part of a spec? |
I don't have a feeling that it makes anything simpler, but who knows.
Kind of. See A.3.3.8 of the Sapling spec. We implemented that as Given that the language targets cryptographers we pretty much have to have loops, because this is what all those crypto algorithms use. Maybe just as a form of syntactic sugar on top of some kind of primitive recursion, though. |
Seems to be done.
Tracked by #69.
Should be fixed by #68 (the compiler is not updated yet, so can't check).
Not going to try that any time soon. |
I was thinking about loops and whether it's possible to have lexical scoping for them at all and I can't see a convenient way of making loops lexically scoped. Variables look to the user as if they were mutable, but internally every assignment creates a new variable, so they're immutable. So just declaring a variable outside of the loop and assigning it a value in the loop is not enough as it will create a new variable at each iteration. We could think of assigning the variable a value only once -- at the final iteration, but
Although both of these problems can be fixed in this particular case, because statically computable things are supposed to have the usual semantics ( Another option would be to have a separate block in a Anyway, just being curious. Non-lexically scoped loops make sense in our case and that's what they use in the Sapling spec. |
One remaining problem with loops is what the value of
finishes. I like It perhaps would be best to remove BTW, what semantics, say,
is supposed to have? |
IMHO in the current implementation the cleanest semantics for for loops is as follows
@effectfully we do not really have variable assignments, just variable bindings, so the program will be written as
Given the above, the semantics of the above loop are as follows:
Still, we should discuss about the following example
as I am still unclear what I guess we could look into how SSA/LLVM handles for loops. Finally, you mentioned that the compiler is using CPS, but as the language contains only bindings, it resembles SSA which is awfully similar to ANF which was touted as an alternative to CPS. |
It's fine, but it doesn't matter if we go with this semantics or with some another one. What matters is to have loops that have an obvious semantics. Currently this is not the case. The easiest option would be just to dump Or maybe we can use C-like I'll think more on that, clearly there's a huge UX problem here.
Yeah, I just managed to forget the syntax.
That doesn't make any sense to me as I expect the loop counter to be incremented by virtue of it being a loop counter. I.e.
is already supposed to increment
The compiler was using CPS for some technical stuff, but it was not the "compiling with CPS" technique, just some |
Here is an interesting program, I suspect that is a bug in the renamer:
We probably should add an assignment operator, because at the moment renamer cannot tell apart a fresh binding with an assignment. We also need to unbind variables after for loop. tack test --ta --quickcheck-replay=397135 |
Err, this was closed automatically when I merge the PR.
Brilliant find. Yes, it's a bug. The renamer assumes that the body of a loop will be evaluated at least once, which it's not the case here, so the renamer thinks that So if we remove all the uniques and look at the program the way the user writes it:
then it's clear that the last mention of Maybe unrolling loops on the language side before calling the renamer was a good idea after all. And in the long term, I wonder how necessary it is to rely on global uniqueness in the compiler and if it's possible to get away without requiring it. I'll think more on that. |
And clearly it has to be a temporary solution, 'cause handling things like
this way in the renaming is just silly. So in the long run it's either
It might actually make sense to do (1). The compiler has a lot of info and can inline things well, but maybe it's an anti-feature in this case, because it does not lead to reliable and predictable UX. Maybe inlining fewer things, but in a predictable way is a better strategy. And then this solves the problem of the loop variable being in scope after the loop, as unrolling a loop amounts to inlining the value of the loop variable in the body of the loop, i.e. we do not create And the compiler does not need to generate any fresh variable names. It's quite appealing to move back to unrolling loops on the language side... |
I have a suspicion that we conflate the notion of binding and assignment, because they are used interchangeably with others. For instance, consider the following program that sums all bits in a let acc = 0;
for i = 0 to n do
let acc = if v[i] then acc else acc + 1;
end; Now it is unclear what the value of Whereas, I propose to introduce an assignment operator let acc = 0;
for i = 0 to n do
acc <- if v[i] then acc else acc + 1;
end; which I belive that it is much readable. Finally, I would want to write the following program let mutable acc = 0
for i = 0 to n do
if v[i] then
acc <- acc + 1 |
For now we decided to unroll loop in the front-end of the compiler. |
Fix #54 by removing for loops from Field.Typed language and unrolling loops in the front-end of the compiler.
Apologies for not responding earlier, I somehow missed this particular comment.
We don't allow statements inside an
It should be the value of the latest But using the exact syntax that the Sapling spec uses and giving it a different meaning is a no go as it would be extremely confusing for our users. I do like the syntax that you proposed, but it just doesn't fit in the context we develop this whole thing in. Now about the general idea. Let's say that
must not unfold into
because then the final But we do unfold loops and since we have to distinguish between the local and global
How will we get there? We can't rename any expression or generally resolve scopes before unfolding loops, because
(the We wouldn't have this problem if it was impossible to declare global variables in a loop body, but this is what the Sapling spec has. Recall that a loop is a just a macro that gets unfolded at some point, it's not a separate construct with its own scope. Overall,
My conclusions are
|
Quoting my comment:
But maybe it's totally fine to track scopes in the type checker and in the renamer. After all, we track them in the evaluator, so why not also do that in the type checker. And relying on variables being globally unique in the type checker would be weird and annoying. And if we don't rely on that, then we're effectively tracking scopes. So yeah, the simplest thing is unroll loops and type check things at the same time and rename afterwards. This is already what is happening, so I'm just convincing myself that this is the right approach here. |
For a long time we had this problem that loops were non-lexically scoped, because that's what they had in the Sapling spec (dunno if they still have non-lexical scoped loops there, but that's now irrelevant) and I did not want to contradict the only source of truth we had. After the recent discussion we decided to move to lexically-scoped loops. I'm going to use a slightly different syntax than the one we've been using so far. Let
Now having lexically-scoped loops means that the following code is ill-typed (by means of being ill-scoped):
because the The right way to write that program is by declaring
This all is great, but making loops lexically-scoped poses some minor challenges. Consider the following program that finds the sum of squares of natural numbers from
Here If we naively compile this IR program to Core, we'll get
Here the local Correct compilation should give us something like this:
Here the local There are a number of ways we could approach this problem:
I'm strongly in favor of 3. How to actually implement the renaming is a pretty technical question and not a hard one (just the usual
and only then inline the blocks and rename variable as appropriate. This is easier to get right and easier to debug than if we do everything at the same time. Plus, if we're going to have lexically-scoped procedures (which are kinda like generalized gadgets), then we will also be able to inline them directly as blocks and worry about scoping separately. So this ask for a tree-like structure of statements in the IR, which we flatten into a list-like structure during compilation to Core. |
Currently we unroll loops during parsing/typechecking. It's probably best to unroll them on the compiler side, since the compiler collects plenty of static info.
This means that we should try adding loops as primitives (i.e. constructors) to both the raw and the typed language. Loop bounds should be arbitrary expressions. The compiler will throw an error if it can't unroll a loop due to loop bounds not being static.
The previous issue talking about loops is #12.
The text was updated successfully, but these errors were encountered: