Drop incompatible constructors in case
This commit is contained in:
3
TODO.md
3
TODO.md
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
I may be done with `U` - I keep typing `Type`.
|
I may be done with `U` - I keep typing `Type`.
|
||||||
|
|
||||||
|
- [ ] eval for case (see order.newt)
|
||||||
|
- [ ] fix `ite.newt`
|
||||||
|
- [ ] dynamic pattern unification (add test case first)
|
||||||
- [x] switch from commit/mustWork to checking progress
|
- [x] switch from commit/mustWork to checking progress
|
||||||
- [x] type constructors are no longer generated? And seem to have 0 arity.
|
- [x] type constructors are no longer generated? And seem to have 0 arity.
|
||||||
- [x] raw let is not yet implemented (although define used by case tree building)
|
- [x] raw let is not yet implemented (although define used by case tree building)
|
||||||
|
|||||||
29
newt/order.newt
Normal file
29
newt/order.newt
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
module Order
|
||||||
|
|
||||||
|
data Nat : U where
|
||||||
|
Z : Nat
|
||||||
|
S : Nat -> Nat
|
||||||
|
|
||||||
|
plus : Nat -> Nat -> Nat
|
||||||
|
plus Z y = y
|
||||||
|
plus (S x) y = S (plus x y)
|
||||||
|
|
||||||
|
data Pair : U -> U -> U where
|
||||||
|
_,_ : {A B : U} -> A -> B -> Pair A B
|
||||||
|
|
||||||
|
infix 1 _,_
|
||||||
|
|
||||||
|
foo : Pair Nat Nat
|
||||||
|
foo = (Z , S Z)
|
||||||
|
|
||||||
|
-- So I was going to force a (a + b, a) =?= (3,0) unification problem
|
||||||
|
-- as an example of needing _dynamic_ pattern unification
|
||||||
|
data Eq : {A : U} -> A -> A -> U where
|
||||||
|
Refl : {A : U} -> {x : A} -> Eq x x
|
||||||
|
|
||||||
|
-- but hold up here. This doesn't solve either.
|
||||||
|
-- Oh, because I can't reduce case.
|
||||||
|
-- And the FC is useless.
|
||||||
|
two : Eq (plus Z (S (S Z))) (S (S Z))
|
||||||
|
two = Refl {Nat} {S (S Z)}
|
||||||
|
|
||||||
@@ -4,6 +4,13 @@ module TypeClass
|
|||||||
|
|
||||||
-- this would be nicer with records and copatterns
|
-- this would be nicer with records and copatterns
|
||||||
|
|
||||||
|
-- At this point, for Agda style, I'll probably need to postpone, or collect constraints
|
||||||
|
-- at lesat. Idris style, I might get away without short term.
|
||||||
|
|
||||||
|
-- So I can read mcbride paper and maybe agda source.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- we need a bit more than this, but
|
-- we need a bit more than this, but
|
||||||
data Monad : (U -> U) -> U where
|
data Monad : (U -> U) -> U where
|
||||||
MkMonad : { M : U -> U } ->
|
MkMonad : { M : U -> U } ->
|
||||||
@@ -15,6 +22,17 @@ data Maybe : U -> U where
|
|||||||
Nothing : {A : U} -> Maybe A
|
Nothing : {A : U} -> Maybe A
|
||||||
|
|
||||||
|
|
||||||
|
data Either : U -> U -> U where
|
||||||
|
Left : {A B : U} -> A -> Either A B
|
||||||
|
Right : {A B : U} -> B -> Either A B
|
||||||
|
|
||||||
|
EitherMonad : {A : U} -> Monad (Either A)
|
||||||
|
EitherMonad = MkMonad {Either A} (\ ma amb =>
|
||||||
|
-- ^ Need this for scrut type to be non-meta
|
||||||
|
case ma of
|
||||||
|
Left a => Left a
|
||||||
|
Right b => amb b)
|
||||||
|
|
||||||
-- [instance]
|
-- [instance]
|
||||||
MaybeMonad : Monad Maybe
|
MaybeMonad : Monad Maybe
|
||||||
-- Agda case lambda might be nice..
|
-- Agda case lambda might be nice..
|
||||||
@@ -44,5 +62,29 @@ ptype Int
|
|||||||
-- Idris gets this by specially treating determining arguments of an auto as "invertible". It then unifies
|
-- Idris gets this by specially treating determining arguments of an auto as "invertible". It then unifies
|
||||||
-- the last arg on each side and tries the rest, which is now in the pattern fragment.
|
-- the last arg on each side and tries the rest, which is now in the pattern fragment.
|
||||||
|
|
||||||
|
-- Agda may do this slightly differently from Idris:
|
||||||
|
-- https://agda.readthedocs.io/en/v2.6.0.1/language/instance-arguments.html#instance-resolution
|
||||||
|
|
||||||
|
-- I think it searches, pulls in all possibilities and tries to unify them into place.
|
||||||
|
-- We'll want to extend our example to what I have in Foo2.agda to test that it finds the right
|
||||||
|
-- one. Maybe look at the source to see if there is any invertible trickery going on?
|
||||||
|
|
||||||
|
-- I know that here, if I fill in the instance, everything works out, so finding the instance that works
|
||||||
|
-- out might be sufficient. That might mean that a instance constraint is a pile of options that get
|
||||||
|
-- winnowed down?
|
||||||
|
|
||||||
|
-- Putting MaybeMonad in there helps the unification
|
||||||
|
-- of Maybe Int =?= ?m6 ?m2 (as Monad Maybe (the type) gives ?m6 is Maybe)
|
||||||
|
-- and MaybeEither would fail after a couple of steps. But it seems expensive/complex
|
||||||
|
-- to have to run the process down for each candidate.
|
||||||
|
|
||||||
|
-- Agda seems complicated, minting fresh metas for bits of potential solutions (which
|
||||||
|
-- may be tossed if the solution is ruled out.)
|
||||||
|
|
||||||
foo : Int -> Maybe Int
|
foo : Int -> Maybe Int
|
||||||
foo x = _>>=_ (Just x) (\ x => Just 10)
|
foo x = _>>=_ {_} {_} {_} {_} (Just x) (\ x => Just 10)
|
||||||
|
-- ^
|
||||||
|
/-
|
||||||
|
So, agda style we'd guess ?m8 is MonadMaybe or MonadEither - agda's "maybe" case
|
||||||
|
|
||||||
|
-/
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import Lib.Syntax
|
|||||||
data Pden = PR Nat Nat (List Nat)
|
data Pden = PR Nat Nat (List Nat)
|
||||||
|
|
||||||
-- IORef for metas needs IO
|
-- IORef for metas needs IO
|
||||||
|
export
|
||||||
forceMeta : Val -> M Val
|
forceMeta : Val -> M Val
|
||||||
forceMeta (VMeta fc ix sp) = case !(lookupMeta ix) of
|
forceMeta (VMeta fc ix sp) = case !(lookupMeta ix) of
|
||||||
(Unsolved pos k xs _) => pure (VMeta fc ix sp)
|
(Unsolved pos k xs _) => pure (VMeta fc ix sp)
|
||||||
@@ -102,7 +102,7 @@ parameters (ctx: Context)
|
|||||||
|
|
||||||
solve : Nat -> Nat -> SnocList Val -> Val -> M ()
|
solve : Nat -> Nat -> SnocList Val -> Val -> M ()
|
||||||
solve l m sp t = do
|
solve l m sp t = do
|
||||||
debug "solve \{show l} \{show m} \{show sp} \{show t}"
|
debug "solve \{show m} lvl \{show l} sp \{show sp} is \{show t}"
|
||||||
meta <- lookupMeta m
|
meta <- lookupMeta m
|
||||||
debug "meta \{show meta}"
|
debug "meta \{show meta}"
|
||||||
ren <- invert l sp
|
ren <- invert l sp
|
||||||
@@ -113,6 +113,13 @@ parameters (ctx: Context)
|
|||||||
solveMeta top m soln
|
solveMeta top m soln
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
|
trySolve : Nat -> Nat -> SnocList Val -> Val -> M ()
|
||||||
|
trySolve l m sp t = do
|
||||||
|
catchError {e=Error} (solve l m sp t) $ (\err => do
|
||||||
|
debug $ showError "" err
|
||||||
|
pure ())
|
||||||
|
|
||||||
|
|
||||||
export
|
export
|
||||||
unify : (l : Nat) -> Val -> Val -> M UnifyResult
|
unify : (l : Nat) -> Val -> Val -> M UnifyResult
|
||||||
|
|
||||||
@@ -160,9 +167,9 @@ parameters (ctx: Context)
|
|||||||
(VMeta fc k sp, VMeta fc' k' sp' ) =>
|
(VMeta fc k sp, VMeta fc' k' sp' ) =>
|
||||||
if k == k' then unifySpine l (k == k') sp sp'
|
if k == k' then unifySpine l (k == k') sp sp'
|
||||||
-- TODO, might want to try the other way, too.
|
-- TODO, might want to try the other way, too.
|
||||||
else solve l k sp (VMeta fc' k' sp') >> pure neutral
|
else trySolve l k sp (VMeta fc' k' sp') >> pure neutral
|
||||||
(t, VMeta fc' i' sp') => solve l i' sp' t >> pure neutral
|
(t, VMeta fc' i' sp') => trySolve l i' sp' t >> pure neutral
|
||||||
(VMeta fc i sp, t' ) => solve l i sp t' >> pure neutral
|
(VMeta fc i sp, t' ) => trySolve l i sp t' >> pure neutral
|
||||||
(VPi fc _ _ a b, VPi fc' _ _ a' b') => [| unify l a a' <+> unify (S l) !(b $$ VVar emptyFC l [<]) !(b' $$ VVar emptyFC l [<]) |]
|
(VPi fc _ _ a b, VPi fc' _ _ a' b') => [| unify l a a' <+> unify (S l) !(b $$ VVar emptyFC l [<]) !(b' $$ VVar emptyFC l [<]) |]
|
||||||
(VVar fc k sp, (VVar fc' k' sp') ) =>
|
(VVar fc k sp, (VVar fc' k' sp') ) =>
|
||||||
if k == k' then unifySpine l (k == k') sp sp'
|
if k == k' then unifySpine l (k == k') sp sp'
|
||||||
@@ -177,8 +184,7 @@ parameters (ctx: Context)
|
|||||||
if k == k' then do
|
if k == k' then do
|
||||||
debug "unify \{show l} spine at \{k} \{show sp} \{show sp'}"
|
debug "unify \{show l} spine at \{k} \{show sp} \{show sp'}"
|
||||||
unifySpine l (k == k') sp sp'
|
unifySpine l (k == k') sp sp'
|
||||||
-- REVIEW - consider forcing?
|
else error fc "vref mismatch \{show k} \{show k'} -- \{show sp} \{show sp'}"
|
||||||
else error emptyFC "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
|
||||||
@@ -343,7 +349,8 @@ updateContext ctx ((k, val) :: cs) = let ix = (length ctx.env `minus` k) `minus`
|
|||||||
-- ok, so this is a single constructor, CaseAlt
|
-- ok, so this is a single constructor, CaseAlt
|
||||||
-- since we've gotten here, we assume it's possible and we better have at least
|
-- since we've gotten here, we assume it's possible and we better have at least
|
||||||
-- one valid clause
|
-- one valid clause
|
||||||
buildCase : Context -> Problem -> String -> Val -> (String, Nat, Tm) -> M CaseAlt
|
-- return Nothing if dcon doesn't unify with scrut
|
||||||
|
buildCase : Context -> Problem -> String -> Val -> (String, Nat, Tm) -> M (Maybe CaseAlt)
|
||||||
buildCase ctx prob scnm scty (dcName, arity, ty) = do
|
buildCase ctx prob scnm scty (dcName, arity, ty) = do
|
||||||
debug "CASE \{scnm} \{dcName} \{pprint (names ctx) ty}"
|
debug "CASE \{scnm} \{dcName} \{pprint (names ctx) ty}"
|
||||||
vty <- eval [] CBN ty
|
vty <- eval [] CBN ty
|
||||||
@@ -359,7 +366,8 @@ buildCase ctx prob scnm scty (dcName, arity, ty) = do
|
|||||||
-- We get unification constraints from matching the data constructors
|
-- We get unification constraints from matching the data constructors
|
||||||
-- codomain with the scrutinee type
|
-- codomain with the scrutinee type
|
||||||
debug "unify dcon dom with scrut"
|
debug "unify dcon dom with scrut"
|
||||||
res <- unify ctx' (length ctx'.env) ty' scty
|
Just res <- catchError {e = Error} (Just <$> unify ctx' (length ctx'.env) ty' scty) (\_ => pure Nothing)
|
||||||
|
| _ => pure Nothing
|
||||||
|
|
||||||
-- Additionally, we constrain the scrutinee's variable to be
|
-- Additionally, we constrain the scrutinee's variable to be
|
||||||
-- dcon applied to args
|
-- dcon applied to args
|
||||||
@@ -397,7 +405,7 @@ buildCase ctx prob scnm scty (dcName, arity, ty) = do
|
|||||||
|
|
||||||
when (length clauses == 0) $ error ctx.fc "Missing case for \{dcName} splitting \{scnm}"
|
when (length clauses == 0) $ error ctx.fc "Missing case for \{dcName} splitting \{scnm}"
|
||||||
tm <- buildTree ctx' (MkProb clauses prob.ty)
|
tm <- buildTree ctx' (MkProb clauses prob.ty)
|
||||||
pure $ CaseCons dcName (map getName vars) tm
|
pure $ Just $ CaseCons dcName (map getName vars) tm
|
||||||
where
|
where
|
||||||
getName : Bind -> String
|
getName : Bind -> String
|
||||||
getName (MkBind nm _ _) = nm
|
getName (MkBind nm _ _) = nm
|
||||||
@@ -528,7 +536,8 @@ buildTree ctx prob@(MkProb ((MkClause fc constraints [] expr) :: cs) ty) = do
|
|||||||
cons <- getConstructors ctx scty
|
cons <- getConstructors ctx scty
|
||||||
alts <- traverse (buildCase ctx prob scnm scty) cons
|
alts <- traverse (buildCase ctx prob scnm scty) cons
|
||||||
|
|
||||||
pure $ Case fc sctm alts
|
-- TODO check for empty somewhere?
|
||||||
|
pure $ Case fc sctm (catMaybes alts)
|
||||||
|
|
||||||
|
|
||||||
showDef : Context -> List String -> Nat -> Val -> M String
|
showDef : Context -> List String -> Nat -> Val -> M String
|
||||||
@@ -554,7 +563,7 @@ check ctx tm ty = case (tm, !(forceType ty)) of
|
|||||||
let ctx' = extend ctx scnm scty
|
let ctx' = extend ctx scnm scty
|
||||||
cons <- getConstructors ctx' scty
|
cons <- getConstructors ctx' scty
|
||||||
alts <- traverse (buildCase ctx' (MkProb clauses ty) scnm scty) cons
|
alts <- traverse (buildCase ctx' (MkProb clauses ty) scnm scty) cons
|
||||||
pure $ Let fc scnm sc $ Case fc (Bnd fc 0) alts
|
pure $ Let fc scnm sc $ Case fc (Bnd fc 0) (catMaybes alts)
|
||||||
|
|
||||||
-- Document a hole, pretend it's implemented
|
-- Document a hole, pretend it's implemented
|
||||||
(RHole fc, ty) => do
|
(RHole fc, ty) => do
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ parseOp = parseApp >>= go 0
|
|||||||
go : Int -> Raw -> Parser Raw
|
go : Int -> Raw -> Parser Raw
|
||||||
go prec left =
|
go prec left =
|
||||||
try (do
|
try (do
|
||||||
op <- token Oper
|
|
||||||
fc <- getPos
|
fc <- getPos
|
||||||
|
op <- token Oper
|
||||||
ops <- getOps
|
ops <- getOps
|
||||||
let op' = "_" ++ op ++ "_"
|
let op' = "_" ++ op ++ "_"
|
||||||
let Just (p,fix) = lookup op' ops
|
let Just (p,fix) = lookup op' ops
|
||||||
|
|||||||
@@ -82,12 +82,15 @@ processDecl (Def fc nm clauses) = do
|
|||||||
|
|
||||||
for_ mc.metas $ \case
|
for_ mc.metas $ \case
|
||||||
(Solved k x) => pure ()
|
(Solved k x) => pure ()
|
||||||
(Unsolved (l,c) k xs ty) => do
|
(Unsolved (l,c) k ctx ty) => do
|
||||||
-- should just print, but it's too subtle in the sea of messages
|
-- should just print, but it's too subtle in the sea of messages
|
||||||
-- we'd also need the ability to mark the whole top context as failure if we continue
|
-- we'd also need the ability to mark the whole top context as failure if we continue
|
||||||
-- put a list of errors in TopContext
|
-- put a list of errors in TopContext
|
||||||
putStrLn $ showError "" $ E (l,c) "Unsolved meta \{show k}"
|
|
||||||
addError $ E (l,c) "Unsolved meta \{show k}"
|
-- Something wrong here - bad VVar
|
||||||
|
tm <- quote ctx.lvl !(forceMeta ty)
|
||||||
|
-- putStrLn $ showError "" $ E (l,c) "Unsolved meta \{show k} type \{show ty}"
|
||||||
|
addError $ E (l,c) "Unsolved meta \{show k} type \{pprint (names ctx) tm}"
|
||||||
-- throwError $ E (l,c) "Unsolved meta \{show k}"
|
-- throwError $ E (l,c) "Unsolved meta \{show k}"
|
||||||
debug "Add def \{nm} \{pprint [] tm'} : \{pprint [] ty}"
|
debug "Add def \{nm} \{pprint [] tm'} : \{pprint [] ty}"
|
||||||
modify $ setDef nm ty (Fn tm')
|
modify $ setDef nm ty (Fn tm')
|
||||||
|
|||||||
18
tests/black/case3.newt
Normal file
18
tests/black/case3.newt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
module Case3
|
||||||
|
|
||||||
|
data Nat : U where
|
||||||
|
Z : Nat
|
||||||
|
S : Nat -> Nat
|
||||||
|
|
||||||
|
data Vect : Nat -> U -> U where
|
||||||
|
Nil : {a : U} -> Vect Z a
|
||||||
|
_::_ : {a : U} -> {k : Nat} -> a -> Vect k a -> Vect (S k) a
|
||||||
|
|
||||||
|
infixr 5 _::_
|
||||||
|
|
||||||
|
head : {a : U} {k : Nat} -> Vect (S k) a -> a
|
||||||
|
head (x :: xs) = x
|
||||||
|
|
||||||
|
zapp : {s t: U} {k : Nat} -> Vect k (s -> t) -> Vect k s -> Vect k t
|
||||||
|
zapp (f :: fs) (t :: ts) = f t :: zapp fs ts
|
||||||
|
zapp Nil Nil = Nil
|
||||||
Reference in New Issue
Block a user