-
Notifications
You must be signed in to change notification settings - Fork 1
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
feat: interpreter (faster evaluator-to-normal-form) #1187
Conversation
5dff54f
to
abc3475
Compare
@dhess: I'll leave some cleanup work for you to look at:
|
9e49321
to
9cfbd77
Compare
Ann _ (PrimCon _ c) (TCon _ ((== primConName c) -> True)) | ||
| Just (CaseBranch _ [] t) <- find ((PatPrim c ==) . caseBranchName) brs -> interp' tydefs env Chk t | ||
| CaseFallback t <- fb -> interp' tydefs env Chk t | ||
| otherwise -> throw $ NoBranch (Right c) $ caseBranchName <$> brs | ||
-- literals (primitive constructors) are actually synthesisable, so may come | ||
-- without annotations | ||
PrimCon _ c | ||
| Just (CaseBranch _ [] t) <- find ((PatPrim c ==) . caseBranchName) brs -> interp' tydefs env Chk t | ||
| CaseFallback t <- fb -> interp' tydefs env Chk t | ||
| otherwise -> throw $ NoBranch (Right c) $ caseBranchName <$> brs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This "duplication" is a bit of a worry. It is reflecting the same in the stepwise evaluator, but we should perhaps review the calculus to see if it is really needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #1237
9cfbd77
to
6c5b007
Compare
We sometimes evaluate a `case` and sometimes an `app`. These should be evaluated with different directions. This was noticed when implementing a more efficient interpreter which gave a different output when evaluating the checkable `case` in a synthesis context. Signed-off-by: Ben Price <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor comments. No code changes.
primer/src/Primer/EvalFullInterp.hs
Outdated
- perhaps do full NBE | ||
- maybe don't stay in AST domain | ||
this perhaps may help with a pure recursion-depth limit and reporting partial results? | ||
-} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should expand on this, maybe in a GitHub issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #1194
6c5b007
to
3487cf3
Compare
I think this is more-or-less ready, except the testsuite run seems to fail on linux CI (OOM??). I'll need to investigate further. Some initial investigation shows that running the CI testsuite ( |
(I think the last OOM was due to a long-running earlier test that was chewing up lots of resident RAM.) |
This OOM is due to 14096bf refactor: generalise WT to be a transformer. (A useful technique here is to run |
|
Yes. It is used in property tests only |
This is needed to be able to run `IO` actions in `PropertyWT`, which will itself be needed when we implement a more efficient evaluator (due to using `System.Timeout` to avoid infinite loops). Another choice would be to generalise `WT` to be a transformer also. However, we only actually use the (hypothetical) `WT IO` and not any other `WT m`. Since testing showed that the extra polymorphism caused GHC to emit much less (memory) efficient code, we will use the monomorphic version. (I expect that sufficient use of specialization pragmas would fix the memory leaks for a transformer version of `WT`, but I have not verified this.) Signed-off-by: Ben Price <[email protected]>
There is no need for this to care about the metadata's type. Signed-off-by: Ben Price <[email protected]>
We will shortly implement a more efficient "full evaluator", and wish to disambiguate some naming. Signed-off-by: Ben Price <[email protected]>
This is to enable reuse in a subsequent commit. Signed-off-by: Ben Price <[email protected]>
This does not iterate the single-step redex evaluator, but instead uses Haskell's runtime to directly interpret primer ASTs. Signed-off-by: Ben Price <[email protected]>
This adds code to check for alpha equality of terms Signed-off-by: Ben Price <[email protected]>
3487cf3
to
c545482
Compare
I have force-pushed a fix for this. Essentially we don't make I think this PR is ready to merge now. @dhess: note that there are two
|
Thanks! The speedup here will make a huge impact. |
Note that the first of these was (at least partially) addressed in #1238, and the second is addressed in #1241. |
The one feature of the old stepwise evaluator that we don't replicate is the ability not to evaluate under lambdas.
This may be annoying if this new interpreter is hooked up to a frontend, as then any evaluation of a recursive function will diverge, even if an evaluation of that same function applied to an argument may succeed. The possible mitigations for this include
Maybe (Bool -> Char)
, or a return type ofT
wheredata T = C (Bool -> Char)
.Expr
spine-strict, since we need to step outside of the AST domain to keep laziness in that case anyway.)When working on this PR, at one point we had an explicit
"fuel" counter so that the interpreter would not loop on a divergent term.
This has been replaced (and rebased) with an IO-based System.Timeout call,
but the old state is still in the history for https://github.com/hackworthltd/primer/tree/brprice/wip/interp.
The reason we changed track here is
Primer (the object language) are lazy
(in particular, code like
head (letrec l = Cons True l in l)
shouldreduce to
True
(ignoring type annotations)ExceptT
(i.e.Either
) to signal the"recursion depth exceeded" error, since to pattern match on the (root
of the) AST of a recursive call would force the
Either
, and thusdivergent subterms would throw a
Left
, aborting the whole computation.error
,or
Control.Exception
sthrow
), from pure code. This is essentiallyhiding an exception deep within a (recursively returned) AST, and will
not actually trigger unless it is forced.
"timeout" error in the testsuite, meaning we need to be able to run IO
actions in Hedgehog tests, and thus need to refactor our WT monad to
(optionally) include IO at the base.
check and use
System.Timeout
instead, which also works inIO
,and avoids some overhead due to not tracking the depth (and makes the
interpretation of recursive let bindings nicer.
Note that (c.f. comment in code) this new interpreter relies heavily on laziness and will break if
Expr
is made spine-strict.