Zulip Chat Archive

Stream: lean4

Topic: Dealing with level params in metaprogramming


Max Nowak πŸ‰ (Feb 19 2023 at 10:32):

I have arg : Type v and inductive T.{u} : Type u -> Type u, and then my expr is T arg, which gets a repr of .app (.const T [v]) (.const arg [v]), so this far everything is working correctly when Lean does it for me.

But in my tactic I want to construct such expressions myself, and I don't know which functions exist to deal with level params like this, nor do I know much theory regarding level params (though I'm guessing this will be some kind of unification problem). Any pointers?

Currently I essentially re-implemented "mkAppN" myself to some degree, since I need to partially apply some function parameters with some specific pattern (context: I'm doing some monomorphization). As such, I have a recursive function which goes through a forall telescope, and in each step does:

def myfunc := do
  Meta.forallBoundedTelescope expr (some 1) fun fvars body => do
    ...
    let body' := body.replaceFvar fvars[0]! arg
    myfunc body'

I can't use mkAppOptM' here since we're dealing with foralls instead of lambdas. But I think the problem stems from the replaceFvar, since arg contains levelParams with name v, which do not exist in the context that I'm working with, and replaceFVar is not smart enough to unify them with anyhing.

Reid Barton (Feb 19 2023 at 10:53):

It sounds like something is wrong with arg already, if it contains universe level parameters that are not in scope

Reid Barton (Feb 19 2023 at 10:54):

I thought the question was going to go in the direction of: how do I work out what universe parameters to pass to T, so that it type checks with arg.

Reid Barton (Feb 19 2023 at 10:56):

I guess the answer to that would be something like: apply T to a fresh level metavariable, and then unify the types before doing replaceFvar.

Max Nowak πŸ‰ (Feb 19 2023 at 10:57):

Hm, yeah, now that you say it, arg does contain universe level parameters that are not in scope. I didn't realize that was the key problem.

I construct my own T' given T, specifically I have an expression T arg₁ argβ‚‚ and want to monomorphize it to T_arg₁ argβ‚‚ for example.

Reid Barton (Feb 19 2023 at 11:01):

Anyways the theory of how universes occur in expressions is pretty simple: only constants (i.e. top-level definitions, instances, etc) can quantify over universe levels, they always do so at the outermost position, and you always specify all the universe parameters when referring to a constant.

Max Nowak πŸ‰ (Feb 19 2023 at 11:02):

Yeah that took me a little while to fully wrap my head around. Helps to imagine they are essentially binders, but since it's so simple, a list with names is enough.

Reid Barton (Feb 19 2023 at 11:04):

It's the same way quantification over types works in standard Haskell, if that helps

Max Nowak πŸ‰ (Feb 19 2023 at 11:07):

I skipped Haskell and went directly for Idris and Lean, unfortunately :P. But I understand how levels work now, just not how to handle them easily in metaprogramming. Currently looking at the metavar approach you suggested.

Max Nowak πŸ‰ (Feb 19 2023 at 11:58):

Can I just disable universe checking? It's actually not very important for what I'm doing.
(Googling "lean unsafe level" did not bring up the results I thought it would xD)

Reid Barton (Feb 19 2023 at 12:04):

Kind of. Do you actually need universe polymorphism at all?
If you're trying to use existential types or something then I think with unsafe you can write inductive types that ignore the universe rules.

Max Nowak πŸ‰ (Feb 19 2023 at 12:07):

I specified isUnsafe := true to addDecl, and it still complains about invalid reference to undeclared universe parameter.

Max Nowak πŸ‰ (Feb 19 2023 at 12:09):

I do not need universe polymorphism at all, in fact, I won't even have polymorphism when I'm done haha. This is all just an intermediate step, and I eventually end up with something I can shove into an SMT solver.

Reid Barton (Feb 19 2023 at 12:23):

Oh, if you are making a new universe-polymorphic definition, then you need to include the parameters in the declaration.

Max Nowak πŸ‰ (Feb 19 2023 at 12:27):

Do you know of a function to collect all of them? There is Lean.collectLevelParams, but it needs to be run in some monad and it feels like some internal function.

Kyle Miller (Feb 19 2023 at 12:52):

I think you're just expected to write (Lean.collectLevelParams {} e).params. I don't know exactly why they're designed this way, but my guess has been that this lets you easily take the union across multiple expressions.

Kyle Miller (Feb 19 2023 at 12:52):

like (Lean.collectLevelParams (Lean.collectLevelParams {} e1) e2).params (or you could imagine a fold across a List or Array).

Max Nowak πŸ‰ (Feb 19 2023 at 12:56):

Ah, yeah, that makes sense I suppose.

Reid Barton (Feb 19 2023 at 14:04):

Depending on what you're doing, you might already know in advance what the universe parameters of the new definition should be

Max Nowak πŸ‰ (Feb 19 2023 at 14:49):

Reid Barton said:

I guess the answer to that would be something like: apply T to a fresh level metavariable, and then unify the types before doing replaceFvar.

I can't quite make out what you meant with this. The problem (as we found earlier today) is that arg already has concrete level params when given to myfunc. How would a metavariable help here?

Reid Barton (Feb 19 2023 at 14:54):

This was meant to be an answer to how to figure out the correct universe parameters for T

Max Nowak πŸ‰ (Feb 19 2023 at 15:32):

Given an inductive type inductive T : Ο„ where | ctors..., in order to find all its levelParams, it is sufficient to check Ο„, right? I don't have to scan the ctors for extra levelParams.

Max Nowak πŸ‰ (Feb 19 2023 at 15:33):

And second question, all ctors have the exact same level params on them as well? (Some small experimentation with #check T.c seems to allude to that)

Max Nowak πŸ‰ (Feb 19 2023 at 15:34):

If the answers to those questions are true, I think I found a somewhat-proper way of dealing with level params, using something like
Lean.instantiateLevelParams T.type T.levelParams levelParamsFromConst.

Max Nowak πŸ‰ (Feb 19 2023 at 15:38):

Oh my dog it works. I can't believe it took me a whole day to figure this out.


Last updated: Dec 20 2023 at 11:08 UTC