Make eval less aggressive about substitution

This commit is contained in:
2024-10-27 12:22:04 -07:00
parent 95a4baf12d
commit e54aced733
6 changed files with 67 additions and 56 deletions

View File

@@ -12,10 +12,12 @@ The goal is to have inductive types, pattern matching, compile to javascript, an
self hosted. Ideally I could build a little browser "playground" to compile, view self hosted. Ideally I could build a little browser "playground" to compile, view
output, and run code. output, and run code.
The repository is tagging `.newt` files as Agda to convince github to highlight them.
## Building ## Building
There is a `Makefile` that builds both chez and javascript versions. They end up in There is a `Makefile` that builds both chez and javascript versions. They end up in
`build/exec` as usual. I've also added a `pack.toml`, so `pack build` `build/exec` as usual. I've also added a `pack.toml`, so `pack build` also works.
There is a vscode extension in `newt-vscode`. Running `make vscode` will build and install it. The extension expects `build/exec/newt` to exist in the workspace. There is a vscode extension in `newt-vscode`. Running `make vscode` will build and install it. The extension expects `build/exec/newt` to exist in the workspace.
@@ -48,6 +50,11 @@ from `Tm` to `Val`. I think I'll need a way to eval a VCase during unification,
Following kovacs, I'm putting `VVar` into context env when I go under binders. This avoids substitution. Following kovacs, I'm putting `VVar` into context env when I go under binders. This avoids substitution.
## Autos
Newt has primitive auto implicits. As a first pass, higher order ones, like monad, will not
## Issues ## Issues
- I should do some erasure of values unused at runtime - I should do some erasure of values unused at runtime

View File

@@ -1,6 +1,14 @@
## TODO ## TODO
NOW - two things
1. I broke Tree.newt by removing some over-aggressive reduction. I need to
make it happen during unification. Not sure if I have all of the bits to vappspine there.
2. Need to collect constraints and re-run, I think that may get me to
typeclasses
- [x] Remember operators from imports - [x] Remember operators from imports
- [ ] Default cases for non-primitives (currently gets expanded to all constructors) - [ ] Default cases for non-primitives (currently gets expanded to all constructors)
- [x] Case for primitives - [x] Case for primitives

View File

@@ -26,34 +26,42 @@ data Either : U -> U -> U where
Left : {A B : U} -> A -> Either A B Left : {A B : U} -> A -> Either A B
Right : {A B : U} -> B -> Either A B Right : {A B : U} -> B -> Either A B
EitherMonad : {A : U} -> Monad (Either A) bindEither : {A B C : U} -> (Either A B) -> (B -> Either A C) -> Either A C
EitherMonad = MkMonad {Either A} (\ ma amb => bindEither (Left a) amb = Left a
-- ^ Need this for scrut type to be non-meta bindEither (Right b) amb = amb b
case ma of
Left a => Left a EitherMonad : {A : U} -> Monad (Either A)
Right b => amb b) EitherMonad = MkMonad {Either A} bindEither
bindMaybe : {A B : U} -> Maybe A -> (A -> Maybe B) -> Maybe B
bindMaybe Nothing amb = Nothing
bindMaybe (Just a) amb = amb a
-- I think it was picking up the Maybe before I made it less aggressive about eval
-- [instance]
MaybeMonad : Monad Maybe MaybeMonad : Monad Maybe
-- Agda case lambda might be nice.. MaybeMonad = MkMonad {Maybe} bindMaybe
-- The {Maybe} isn't solved in type for the case
MaybeMonad = MkMonad {Maybe} (\ {A} ma amb =>
case ma of
Nothing => Nothing
-- It doesn't discover pat$5 is A during pattern matching
-- oh, but var 0 value is var5
Just a => amb a)
-- So the idea here is to have some implicits that are solved by search -- So the idea here is to have some implicits that are solved by search
_>>=_ : {a b : U} -> {m : U -> U} -> {x : Monad m} -> (m a) -> (a -> m b) -> m b -- Error here is from foo, with a bad FC. I think it might be showing up during search.
_>>=_ {a} {b} {m} {MkMonad bind'} ma amb = bind' {a} {b} ma amb _>>=_ : {a b : U} -> {m : U -> U} -> {{x : Monad m}} -> (m a) -> (a -> m b) -> m b
_>>=_ {a} {b} {m} {{MkMonad bind'}} ma amb = bind' {a} {b} ma amb
infixl 1 _>>=_ infixl 1 _>>=_
ptype Int ptype Int
-- For now, we may try to solve this at creation time, but it's possible postpone is needed -- It's bailing on MaybeMonad and picking up Either?
foo : Int -> Maybe Int
foo x = (Just x) >>= (\ x => Just 10)
-- NOW
-- Older notes below, but this is _close_, we're doing something like agda, and we're getting the right solution
-- It's over-expanded, and there is an unsolved meta that I think the solution would unlock. (Need to collect and
-- retry constraints)
-- *SOLVE meta 6 sp [< (%var 0 [< ]), (%meta 4 [< (%var 0 [< ])])] val (%ref Maybe [< (%meta 9 [< (%var 0 [< ]), (%var 1 [< ])])]) -- *SOLVE meta 6 sp [< (%var 0 [< ]), (%meta 4 [< (%var 0 [< ])])] val (%ref Maybe [< (%meta 9 [< (%var 0 [< ]), (%var 1 [< ])])])
@@ -81,8 +89,7 @@ ptype Int
-- Agda seems complicated, minting fresh metas for bits of potential solutions (which -- Agda seems complicated, minting fresh metas for bits of potential solutions (which
-- may be tossed if the solution is ruled out.) -- may be tossed if the solution is ruled out.)
foo : Int -> Maybe Int
foo x = _>>=_ {_} {_} {_} {_} (Just x) (\ x => Just 10)
-- ^ -- ^
/- /-
So, agda style we'd guess ?m8 is MonadMaybe or MonadEither - agda's "maybe" case So, agda style we'd guess ?m8 is MonadMaybe or MonadEither - agda's "maybe" case

View File

@@ -125,13 +125,15 @@ parameters (ctx: Context)
-- REVIEW is this the right fc? -- REVIEW is this the right fc?
then error fc "meta occurs check" then error fc "meta occurs check"
else goSpine ren lvl (Meta fc ix) sp else goSpine ren lvl (Meta fc ix) sp
go ren lvl (VLam fc n t) = pure (Lam fc n !(go (lvl :: ren) (S lvl) !(t $$ VVar emptyFC lvl [<]))) go ren lvl (VLam fc n t) = pure (Lam fc n !(go (lvl :: ren) (S lvl) !(t $$ VVar fc lvl [<])))
go ren lvl (VPi fc n icit ty tm) = pure (Pi fc n icit !(go ren lvl ty) !(go (lvl :: ren) (S lvl) !(tm $$ VVar emptyFC lvl [<]))) go ren lvl (VPi fc n icit ty tm) = pure (Pi fc n icit !(go ren lvl ty) !(go (lvl :: ren) (S lvl) !(tm $$ VVar emptyFC lvl [<])))
go ren lvl (VU fc) = pure (U fc) go ren lvl (VU fc) = pure (U fc)
-- for now, we don't do solutions with case in them. -- for now, we don't do solutions with case in them.
go ren lvl (VCase fc sc alts) = error fc "Case in solution" go ren lvl (VCase fc sc alts) = error fc "Case in solution"
go ren lvl (VLit fc lit) = pure (Lit fc lit) go ren lvl (VLit fc lit) = pure (Lit fc lit)
go ren lvl (VLet fc {}) = error fc "rename Let not implemented" go ren lvl (VLet fc name val body) =
pure $ Let fc name !(go ren lvl val) !(go (lvl :: ren) (S lvl) !(body $$ VVar fc lvl [<]))
lams : Nat -> Tm -> Tm lams : Nat -> Tm -> Tm
lams 0 tm = tm lams 0 tm = tm
@@ -141,9 +143,9 @@ parameters (ctx: Context)
export export
solve : (lvl : Nat) -> (k : Nat) -> SnocList Val -> Val -> M () solve : (lvl : Nat) -> (k : Nat) -> SnocList Val -> Val -> M ()
solve l m sp t = do solve l m sp t = do
debug "solve \{show m} lvl \{show l} sp \{show sp} is \{show t}" meta@(Unsolved metaFC ix ctx ty kind) <- lookupMeta m
meta@(Unsolved metaFC ix ctx ty _) <- lookupMeta m
| _ => error (getFC t) "Meta \{show m} already solved!" | _ => error (getFC t) "Meta \{show m} already solved!"
debug "SOLVE \{show m} \{show kind} lvl \{show l} sp \{show sp} is \{show t}"
let size = length $ filter (\x => x == Bound) $ toList ctx.bds let size = length $ filter (\x => x == Bound) $ toList ctx.bds
debug "\{show m} size is \{show size}" debug "\{show m} size is \{show size}"
if (length sp /= size) then do if (length sp /= size) then do
@@ -234,15 +236,21 @@ parameters (ctx: Context)
-- REVIEW - consider separate value for DCon/TCon -- REVIEW - consider separate value for DCon/TCon
(VRef fc k def sp, VRef fc' k' def' sp' ) => (VRef fc k def sp, VRef fc' k' def' sp' ) =>
if k == k' then do -- This is a problem for cmp (S x) (S y) =?= cmp x y, when the
debug "unify \{show l} spine at \{k} \{show sp} \{show sp'}" -- same is an equation in cmp.
unifySpine l (k == k') sp sp'
else do -- if k == k' then do
-- debug "unify \{show l} spine at \{k} \{show sp} \{show sp'}"
-- unifySpine l (k == k') sp sp'
-- else
do
Nothing <- tryEval k sp Nothing <- tryEval k sp
| Just v => unify l v u' | Just v => unify l v u'
Nothing <- tryEval k' sp' Nothing <- tryEval k' sp'
| Just v => unify l t' v | Just v => unify l t' v
error fc "vref mismatch \{show k} \{show k'} -- \{show sp} \{show sp'}" if k == k'
then unifySpine l (k == k') sp sp'
else error fc "vref mismatch \{show k} \{show k'} -- \{show sp} \{show sp'}"
(VU _, VU _) => pure neutral (VU _, VU _) => pure neutral
-- Lennart.newt cursed type references itself -- Lennart.newt cursed type references itself
@@ -745,8 +753,8 @@ buildTree ctx prob@(MkProb ((MkClause fc constraints [] expr) :: cs) ty) = do
case pat of case pat of
PatCon _ _ _ _ => do PatCon _ _ _ _ => do
-- expand vars that may be solved -- expand vars that may be solved, eval top level functions
scty' <- unlet ctx scty scty' <- unlet ctx scty >>= forceType
debug "EXP \{show scty} -> \{show scty'}" debug "EXP \{show scty} -> \{show scty'}"
-- this is per the paper, but it would be nice to coalesce -- this is per the paper, but it would be nice to coalesce
-- default cases -- default cases

View File

@@ -74,24 +74,8 @@ bind v env = v :: env
-- TODO maybe add glueing -- TODO maybe add glueing
-- So this is a tricky bit - I don't want to expand top level functions
-- if I can't get past the case tree. Kovacs' eval doesn't have the spine
-- when it starts applying. So I'll collect a spine as soon as I see an App
-- Try to apply the Ref, and fall back to vappSpine.
evalSpine : Env -> Mode -> Tm -> List Val -> M Val
evalSpine env mode (App _ t u) sp = evalSpine env mode t (!(eval env mode u) :: sp)
evalSpine env mode (Ref fc nm (Fn tm)) sp = do
v <- eval env mode tm
let sp' = [<] <>< sp
case !(vappSpine v sp') of
(VCase x sc xs) => pure $ VRef fc nm (Fn tm) sp'
v => pure v
evalSpine env mode tm sp = vappSpine !(eval env mode tm) ([<] <>< sp)
-- This is too aggressive...
-- eval env mode (Ref _ x (Fn tm)) = eval env mode tm
eval env mode (Ref fc x def) = pure $ VRef fc x def [<] eval env mode (Ref fc x def) = pure $ VRef fc x def [<]
eval env mode (App _ t u) = evalSpine env mode t [!(eval env mode u)] eval env mode (App _ t u) = vapp !(eval env mode t) !(eval env mode u)
eval env mode (U fc) = pure (VU fc) eval env mode (U fc) = pure (VU fc)
eval env mode (Meta fc i) = eval env mode (Meta fc i) =
case !(lookupMeta i) of case !(lookupMeta i) of

View File

@@ -22,10 +22,6 @@ isCandidate (VRef _ nm _ _) (Ref fc nm' def) = nm == nm'
isCandidate ty (App fc t u) = isCandidate ty t isCandidate ty (App fc t u) = isCandidate ty t
isCandidate _ _ = False isCandidate _ _ = False
-- go : List Binder -> Tm -> (Tm, List Binder)
-- go ts (Pi fc nm icit t u) = go (MkBind fc nm icit t :: ts) u
-- go ts tm = (tm, reverse ts)
-- This is a crude first pass -- This is a crude first pass
-- TODO consider ctx -- TODO consider ctx
@@ -44,10 +40,11 @@ findMatches ty ((MkEntry name type def@(Fn t)) :: xs) = do
debug "TRY \{name} : \{pprint [] type} for \{show ty}" debug "TRY \{name} : \{pprint [] type} for \{show ty}"
tm <- check (mkCtx top.metas fc) (RVar fc name) ty tm <- check (mkCtx top.metas fc) (RVar fc name) ty
debug "Found \{pprint [] tm} for \{show ty}" debug "Found \{pprint [] tm} for \{show ty}"
(tm ::) <$> findMatches ty xs)
(\ _ => do
writeIORef top.metas mc writeIORef top.metas mc
debug "No match \{show ty} \{pprint [] type}" (tm ::) <$> findMatches ty xs)
(\ err => do
debug "No match \{show ty} \{pprint [] type} \{showError "" err}"
writeIORef top.metas mc
findMatches ty xs) findMatches ty xs)
findMatches ty (y :: xs) = findMatches ty xs findMatches ty (y :: xs) = findMatches ty xs