Zulip Chat Archive
Stream: iris-lean
Topic: Porting iProp to typeclasses
Markus de Medeiros (Sep 28 2025 at 15:32):
@Mario Carneiro I'm getting kind of stuck on some Lean details for porting iProp and I'm wondering if you might have any ideas on how to proceed.
Basically, iProp in Rocq does equational reasoning about a global list of RFunctors, using a typeclass to specify that the ith entry in the list gives a specific CMRA when applied to the fixpoint iProp. This is straightforward to specify in Rocq since they are using bundled structures. Remy tried porting it the same way (bundling RFunctor instances in the list of GFunctors), but this leads to a bunch of CMRA transports I was not sure how to deal with.
The central issue is that if we need to be able to relate lemmas about a particular CMRA, say ConstOF Unit (IProp FF) (IProp FF), to the iProp constructions where the typeclass instances come from an IsGFunctors instance. I tried another approach here where I used a typeclass to specify what the ith entry in the list of OFunctorPre's is, though this also leads to CMRA's not being defeq that I would expect to be so.
Do you have any ideas about the right way to mechanize this? I can't think of a good way which allows us to describe the global list of OFunctors using equations.
Mario Carneiro (Oct 03 2025 at 15:53):
I've been wanting to refactor the RFunctors thing to avoid needing an explicit finite list of functors and instead do something more abstract like a type (which will ultimately be instantiated as a finite product of CMRAs) from which individual CMRAs can be projected, with each projection being its own typeclass (or maybe one typeclass parameterized by the CMRA to project out). This should eliminate the equational reasoning
Mario Carneiro (Oct 03 2025 at 16:04):
I'm looking at the file now and I am not sure how much of this is your work vs what is in iris
Mario Carneiro (Oct 03 2025 at 16:05):
I assume you want to fix the sorries in that file?
Markus de Medeiros (Oct 03 2025 at 16:05):
My file is mostly my work. Remy's fork is closer to Iris (bundled structures, etc)
Markus de Medeiros (Oct 03 2025 at 16:06):
The sorry's are one thing but I think the problem I'm describing is exemplified by the example on line 55 (the rfl fails)
Markus de Medeiros (Oct 03 2025 at 16:09):
I'm not sure I understand how your idea allows us to have impredicative resources, the construction is not abstract specifically for that purpose?
Mario Carneiro (Oct 03 2025 at 16:10):
I might be missing a design constraint, can you explain more about that?
Markus de Medeiros (Oct 03 2025 at 16:13):
Like, the Iris construction uses RFunctors for UPred and gmap so that the fixpoint is a UPred resource, whose UCMRA is a map, and where each projection of this map is a particular RFunctor applied to the fixpoint. Solving the fixpoint and is integrated with how the map of resources are represented.
Mario Carneiro (Oct 03 2025 at 16:15):
Markus de Medeiros said:
The sorry's are one thing but I think the problem I'm describing is exemplified by the example on line 55 (the rfl fails)
I thought a bit about this, and I think it's expected behavior. GFunctors is essentially a map keyed by type, which means when you have a type which is not unique or in this case overlaps a general purpose type then you get overlapping instances. These aren't even equal, not just not defeq
Markus de Medeiros (Oct 03 2025 at 16:15):
Agreed
Mario Carneiro (Oct 03 2025 at 16:15):
The solution is to ensure that any functor you use in a GFunctors is specifically intended for use in one
Mario Carneiro (Oct 03 2025 at 16:16):
i.e. don't put ConstOF Unit in a GFunctors directly, put MyFunctor (ConstOF Unit) instead
Mario Carneiro (Oct 03 2025 at 16:19):
Markus de Medeiros said:
Like, the Iris construction uses RFunctors for
UPredandgmapso that the fixpoint is aUPredresource, whose UCMRA is a map, and where each projection of this map is a particularRFunctorapplied to the fixpoint. Solving the fixpoint and is integrated with how the map of resources are represented.
This is fine I think, the ultimate implementation can still be a finite list of functors as before. You just abstract that in the API so that this is only visible in the construction and at the end when you "tie the knot" to prove that there exists some GFunctors which contains four specific functors corresponding to various modalities you wanted to use in your heaplang etc
Mario Carneiro (Oct 03 2025 at 16:19):
and we can provide a macro to do the latter part
Mario Carneiro (Oct 03 2025 at 16:20):
For the in between stuff we just have a typeclass saying that you can somehow extract a MyFunctor from FF which is used in the myModality theory file
Markus de Medeiros (Oct 03 2025 at 16:21):
What is MyFunctor supposed to mean?
Mario Carneiro (Oct 03 2025 at 16:22):
That's ultimately FF 2 : MyFunctor extracted from FF : GFunctors
Mario Carneiro (Oct 03 2025 at 16:22):
but we hide all the details about the fact that we are applying a function or what index it is
Mario Carneiro (Oct 03 2025 at 16:23):
I believe MyFunctor is the F in ElemG FF τ F in your code
Mario Carneiro (Oct 03 2025 at 16:24):
instantiated in the particular case for some specific F
Markus de Medeiros (Oct 03 2025 at 16:25):
Mario Carneiro said:
i.e. don't put
ConstOF Unitin a GFunctors directly, putMyFunctor (ConstOF Unit)instead
I'm not sure how to reconcile that with this
Mario Carneiro (Oct 03 2025 at 16:29):
Let's say you want to have a GFunctors containing functors MyF1, MyF2, MyF3 which each give you some kind of capability. There will be a file talking about MyF1 and the operations associated with having one; this will have a typeclass assumption [ElemG FF MyF1] (I don't think it is necessary to expose the τ parameter in the typeclass). Most of the work will therefore have assumptions [ElemG FF MyF1] [ElemG FF MyF2] [ElemG FF MyF3], and then there is a macro which constructs some MyFF definition with instances ElemG MyFF MyF1, ElemG MyFF MyF2, ElemG MyFF MyF3
Mario Carneiro (Oct 03 2025 at 16:31):
you would never write [ElemG FF (ConstOF Unit)] because this would cause a diamond problem with the pre-existing ConstOF Unit instance
Markus de Medeiros (Oct 03 2025 at 16:36):
How do we specify that we have access to ConstOF Unit resources in this system?
Mario Carneiro (Oct 03 2025 at 16:36):
maybe I misunderstood, but I think the Inst1 instance says that ConstOF Unit resources exist in every system?
Mario Carneiro (Oct 03 2025 at 16:37):
aha, I did misunderstand
Mario Carneiro (Oct 03 2025 at 16:37):
instCMRAElemG is a bad instance, you should always have an alternative route to prove that
Markus de Medeiros (Oct 03 2025 at 16:40):
I agree that it is a problematic instance I just don't really see a way around it...
Mario Carneiro (Oct 03 2025 at 16:40):
Why are GFunctors and IsGFunctors separate?
Mario Carneiro (Oct 03 2025 at 16:40):
the name is not great
Mario Carneiro (Oct 03 2025 at 16:40):
because it's not obvious what it means to assert that a GFunctors IsGFunctors, it sounds like it should always be the case
Markus de Medeiros (Oct 03 2025 at 16:40):
Definitely agreed
Mario Carneiro (Oct 03 2025 at 16:41):
moreover it seems IsGFunctors is additional dependent data
Markus de Medeiros (Oct 03 2025 at 16:42):
Right. So should we use bundled structures for this then?
Mario Carneiro (Oct 03 2025 at 16:42):
I guess it's the actual instance and stuff, more than just the type
Mario Carneiro (Oct 03 2025 at 16:42):
yeah, I think it would make sense to bundle GFunctors
Mario Carneiro (Oct 03 2025 at 16:43):
we don't want to use its projections directly as discussed anyway
Mario Carneiro (Oct 03 2025 at 16:44):
we instead want to use separate variables which already have instances of their own (like F := ConstOF Unit), with a typeclass saying that they can be extracted from FF
Mario Carneiro (Oct 03 2025 at 16:44):
which is where we hide the equality assumption
Mario Carneiro (Oct 03 2025 at 16:45):
OH I get it now, ElemG is taking a lawless GFunctors so it's actually wrong
Mario Carneiro (Oct 03 2025 at 16:46):
it should take both the GFunctors and IsGFunctors because it needs to assert that the cmra part is also equal to the pre-existing cmra on F
Mario Carneiro (Oct 03 2025 at 16:46):
(or alternatively bundle everything)
Markus de Medeiros (Oct 03 2025 at 16:51):
Yeah, right. This is the tricky part though. I tried adding a CMRA equality constraint to the ElemG typeclass at one point and having to rewrite by equations for both the CMRA and the type on that CMRA was difficult. I also couldn't figure out how to relate lemmas about the base CMRA (for example, validN unit) to the versions specified by ElemG.
I will revisit the bundling idea then.
Mario Carneiro (Oct 03 2025 at 16:51):
bundling will definitely make it easier to write that equality
Markus de Medeiros (Oct 03 2025 at 16:53):
OK. To give concrete code, Remy was working on that here.
Mario Carneiro (Oct 03 2025 at 16:53):
Markus de Medeiros said:
I also couldn't figure out how to relate lemmas about the base
CMRA(for example,validN unit) to the versions specified byElemG.
You shouldn't need to do this, you should only ever be using the CMRA structure coming from F and not the one you can obtain by transport out of GF i
Markus de Medeiros (Oct 03 2025 at 16:53):
But the lemmas in the algebra library are (rightly) are written in terms of the regular CMRA's
Mario Carneiro (Oct 03 2025 at 16:54):
exactly, that's why you use F which is a regular CMRA
Mario Carneiro (Oct 03 2025 at 16:54):
we might need a bit of automation to make it easy to declare newtype CMRAs
Mario Carneiro (Oct 03 2025 at 16:56):
if all of the GFunctor types are different (and I don't see why you would want to have the same type twice since the GMap already lets you make as many instances of a given type as you want) then it should be fine even to not use newtypes and just use the types you actually want
Markus de Medeiros (Oct 03 2025 at 16:58):
I think I'm starting to get on the same page as you, though at some level the own primitive will need to be written in terms of the projected-out CMRA GF i
Mario Carneiro (Oct 03 2025 at 16:58):
if you have some relevant half written code I can try to be more concrete
Mario Carneiro (Oct 03 2025 at 16:59):
I don't like this argument v : FF τ (IProp FF) (IProp FF)
Mario Carneiro (Oct 03 2025 at 16:59):
it's exposing FF τ
Mario Carneiro (Oct 03 2025 at 17:02):
I think it should look like
def iOwn {GF F} [OFunctor F] [ElemG GF F] (γ : GName) (v : F.ap (IProp GF)) : IProp GF
Markus de Medeiros (Oct 03 2025 at 17:05):
I get it now (though here we would also need a [IsGFunctors GF] instance, or to be bundled, but I believe that either of those are possible).
Mario Carneiro (Oct 03 2025 at 17:05):
using this definition of ElemG
def BundledGFunctors := GType → Σ F : COFE.OFunctorPre, OFunctor F
class ElemG (FF : BundledGFunctors) (F : OFunctorPre) [OFunctor F] where
τ : GType
transp : FF τ = ⟨F, ‹_›⟩
Markus de Medeiros (Oct 03 2025 at 17:08):
Yes. So then under the hood iOwn will be the singleton map where the γth entry of theτth entry of the ghost map is the transported version of v.
Mario Carneiro (Oct 03 2025 at 17:08):
(you might need F explicit in iOwn, not sure if that will always be inferred correctly)
Mario Carneiro (Oct 03 2025 at 17:10):
yeah, I'm not sure which side of the ElemG abstraction results in shorter code for iSingleton
Mario Carneiro (Oct 03 2025 at 17:10):
but I think the approach you are taking there basically makes sense
Mario Carneiro (Oct 03 2025 at 17:11):
at some point you obviously do need to do something with that equality argument
Mario Carneiro (Oct 03 2025 at 17:12):
there is already a cast in iSingleton so maybe it would be better to have the cmra cast there too
Markus de Medeiros (Oct 03 2025 at 17:13):
Yeah okay, this makes sense to me. Thanks for hashing it out!
Markus de Medeiros (Oct 03 2025 at 17:15):
One last thing, do you have any tricks for working with cast? It is difficult to work with, my best attempts have relied on using the subst tactic basically whenever I could though this is a little inelegant.
Mario Carneiro (Oct 03 2025 at 17:15):
that's the universal technique for this
Mario Carneiro (Oct 03 2025 at 17:16):
make sure one side of your equality is a variable, then subst it until the equality is refl and the casts will go away. This is how you prove anything about a cast
Mario Carneiro (Oct 03 2025 at 17:16):
if you are very lucky you might have some other kind of compositional abstraction which can push casts around like eqToHom. I don't think we are lucky in this case
Markus de Medeiros (Oct 03 2025 at 17:18):
Understood, thanks!
Markus de Medeiros (Oct 10 2025 at 19:57):
I still have quite a few sorry's to work through on my branch but now I think we are getting close to something that looks a lot like iProp :)
Screenshot 2025-10-10 at 3.54.40 PM.png
Last updated: Dec 20 2025 at 21:32 UTC