Hi Stefan

I hope you don't mind me copying this reply to the Epigram list: you've raised an interesting topic, and I expect that others may also have useful things to say. (Also, sorry to be slow in response: TYPES and MSFP business are taking up most of my time these days.)

Stefan Karrmann wrote:

Dear Mr. Conor McBride,

you have suggested the notion of a level parameter for declarations. E.g.:

Level n
  data nat = zero | succ nat

How should this be used?
Has nat the type 'forall n:nat, Type n' (in Coq notation)?
Can we apply nat only to constants or to arbitrary nats?


The key thing to achieve is /polymorphism/ in universe levels. My favourite example of the problem is that certainly in Lego and Cayenne (and I believe in Coq also) you may write

 cons Nat (cons Bool nil) : List Type

and List's node in the universe graph will rise higher than that of Nat or Bool, but

 cons (List Nat) (cons (List Bool) nil)

can never typecheck. This isn't just an artificial problem: Yorck Hünke and I tripped over it whilst trying zipWith: map List ts just didn't work; we had to introduce 'big' and 'small' lists, duplicating functionality. The point is that we dont 'want Lists at /some/ level, we want /every/ level to be closed under formation of List types, just as every level is closed under forall.

How to achieve this? Well, I'm not exactly sure, and I don't have a viable system of rules just yet, but I think the key idea comes from Harper and Pollack and it's to make definitions 'Hindley-Milner' polymorphic in universe levels. The good news is that Epigram 2 no longer has generative datatype declarations; like in Cayenne, 'data' is just sugar for a common pattern of definition, so universe polymorphism for definitions covers datatypes as well.

Now, at least in the first instance, this polymorphism will not be expressed by the ordinary 'forall' or quantify over the ordinary 'nat', in much the way that ML type schemes are not types. So it will be the case that 'nat : Type n' for every n, but not that 'nat : forall n : nat. Type n' (which does rather beg the question, anyway).

The bad news: cumulativity and polymorphism don't play so well together. It's not impossible, but maintaining a global graph of universe constraints and checking for cycles can become a lot of hard work. I'm interested to investigate a more restricted, hopefully more tractable, perhaps more explicit system. I'm not in a position to be precise just yet, but I think it's worth considering making each universe level closed under forall /on the nose/, rather than by taking the max level of the domain and range, then introducing an explicit embedding from one level to the next. I'd argue that /relative/ universe distinctions are actually quite significant and do deserve some notation. This would make types behave quite like the /names of types/ in UTT with Tarski-style universes. It simplifies the polymorphism immensely: every definition is polymorphic in its base level, level unification is just 'max'.

I don't know if that's too restrictive; I think we should be willing to experiment with various approaches, and I'm keen to hear what other people have been doing. I don't know exactly what needs to be explicit and where, but I strongly suspect that our new explicitly bidirectional approach to typechecking will shed some light on this. Epigram 2's core theory explicitly separates the terms for which we infer types from the terms whose type can only be checked. Elimination forms still require head-normalisation of the target's type (eg, application happens to functions, projection to pairs,...) but the full conversion rule happens /only/ when the direction changes and we need that the inferred type of a term coincides with the type we are trying to check. It is here that we know both the inferred and required types of something, so we could very well allow lifting coercions, rather than an exact match.

Some readers may be disappointed that I'm not talking about using the datatype of natural numbers in all its computational for universe levels, let alone more exotic well-founded orderings. I think we should walk before we run. I'm not opposed to level-generic programming in principle, but we've got enough work to do if we even want level-parametric programming to work well. What we need is not just theory, but /design/.

All the best

Conor

Reply via email to