123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- Some notes on inductive families
- --------------------------------
- ** Syntax
- The syntax for patterns which are instantiated by type checking (instantiated
- or dot patterns) is ".p". For instance,
- subst .x x refl px = px
- or
- map .zero f [] = []
- map .(suc n) f (x :: xs) = f x :: map n f xs
- In the second example there's some subtle things. The n looks as though it's
- bound in the dot pattern. This is impossible since the dot patterns will be
- thrown away after type checking. What should happen is that the hidden argument
- to _::_ gets the name n and that's where the binding happens.
- This poses a problem for scope checking. The dot pattern can be an arbitrary
- term, but it might contain unbound variables. The scope checker will have to
- bind unbound variables. Maybe that's not a problem?
- The problem is: how to implement scope checking without copy-pasting between the
- ToAbstract instance and the BindToAbstract instance for expressions?
- Generalising a bit does the trick.
- Come to think of it, binding variables in dot patterns is a bad idea. It makes
- the type checking much harder: how to type check a dot pattern (in which
- context). So the cons case above will have to be one of these two:
- map .(suc _) f (x :: xs) = f x :: map _ f xs
- map .(suc n) f (_::_ {n} x xs) = f x :: map n f xs
- ** Type checking
- Step 0: Type checking the datatype
- Nothing strange. We just lift some of the previous restrictions on datatypes.
- Step 1: Type checking the pattern
- Two interesting differences from the ordinary type checking:
- addFirstOrderMeta (α : A)
- ───────────────────────── same for dot patterns
- Γ ⊢ _ : A --> Γ ⊢ α, α
- c : Δ -> Θ -> D xs ss' Γ ⊢ ps : Θ[ts] --> Γ' ⊢ us, αs Γ' ⊢ ss = us : Θ[ts]
- ──────────────────────────────────────────────────────────────────────────────
- Γ ⊢ c ps : D ts ss --> Γ' ⊢ c ts us, αs
- Interaction between first order metas and η-expansion?
- Suppose
- data D : (N -> N) -> Set where
- id : D (\x -> x)
- h : (f : N -> N) -> D f -> ..
- h _ id
- Now we have to check α = \x -> x : N -> N which will trigger η-expansion and
- we'll end up with α x = x : N which we can't solve.
- We'll ignore this for now. Possible solution could be to distinguish between
- variables introduced by η-expansion and variables bound in the pattern.
- Step 2: Turn unsolved metas into bound variables
- - make sure that there are no unsolved constraints from type checking the
- patterns (if so, fail)
- - we need to remember where the first order metas come from, or at least the
- order in which they are generated, so type checking should produce a list of
- first order metas
- - how to get the context in the right order? explicit variables have been
- added to the context but not implicit ones. we should probably make sure
- that the final context is the right one (otherwise reduction will not work
- properly).
- - example:
- f y _ (cons _ x xs)
- the context after type checking is (y x : A)(xs : List A) with meta
- variables (α β : N), where α := suc β. We'd want the final pattern to be
- f y .(suc n) (cons n x xs)
- and the context in the right hand side (y : A)(n : N)(x : A)(xs : List A).
- Solution:
- - pull out the context (y x : A)(xs : List A) and the meta context
- (α := suc β : N)(β : N) and traverse the pattern again, building the right
- context, instantiating uninstantiated metas to fresh variables.
- Quick solution:
- - re-type check the pattern when we know which patterns are dotted and which
- are variables. This also gets rid of (some of) the tricky deBruijn
- juggling that comes with the first-order metas.
- - Problem: when we say
- subst ._ _ refl
- could this mean
- subst x .x refl ?
- Answer: no, an explicit underscore can never become dotted. But there is a
- similar problem in
- tail ._ (x :: xs)
- Here we might instantiate the hidden length in the _::_ rather than the
- dotted first argument. So we need to keep track of first-order metas that
- 'wants' to be instantiated, and instantiate those at higher priority than
- others.
- Why is this a problem? The user won't be able to tell (at least not easily)
- that she got
- tail n (_::_ .{n} x xs)
- rather than
- tail .n (_::_ {n} x xs)
- The problem is rather an implementation problem. We want to check that
- dotted patterns actually get instantiated, and give an error otherwise.
- How would we distinguish this case from the bad cases?
- Step 3: Type check dot patterns and compare to the inferred values
- * after step 2 the context will be the correct one.
- * where do we find the type to check against? look at the meta variables
- generated during type checking
- So,
- - traverse the pattern with the list of meta-variables
- - for each dot pattern,
- + look up the type of the corresponding meta
- + and check that it's equal to the meta-variable
- A BETTER SOLUTION
- ─────────────────
- Context splitting a la Thierry.
- For each clause, generate a case tree. Each case is an operation on the context:
- (case x of C ys : D ss -> t) corresponds to
- (Δ₁, x : D ts, Δ₂) ─→ (Δ₁, ys : Γ, Δ₂[C ys/x])σ
- where σ = unify(ss, ts)
- So to type check a clause:
- ∙ generate case tree
- ∙ perform the context splitting (remembering the substitutions σ)
- ∙ verify that σ corresponds exactly to the dotted patterns
- Questions:
- ∙ what is the unification algorithm?
- ∙ what argument to split on?
- ∙ first constructor pattern? Consider:
- data D : Set -> Set where
- nat : D Nat
- bool : D Bool
- f : (A : Set) -> A -> D A -> X
- f .Nat zero nat = x
- Here we can't split on zero first, since the type is A.
- ∙ first constructor pattern whose type is a datatype
- error if there are constructor patterns left but no argument can be split
- vim: tw=80 sts=2 sw=2 fo=tcq
|