[Agda] Records and irrelevant fields

Dan Doel dan.doel at gmail.com
Fri Sep 17 20:37:48 CEST 2010


On Friday 17 September 2010 8:23:04 am Andreas Abel wrote:

> I thought about the Squash type, but my gut feeling was it won't
> provide a nice solution to the problem.

Yeah, probably not.

> Well, but is not the whole point of EPTS's that types can depend on
> irrelevant stuff where programs cannot? If both cannot, we can go back
> to Pfenning's 2001 LiCS system where things work smoothly (but imho,
> boringly).

Yes. And perhaps modifying the erasure within terms is the right way to go.

Currently, it seems to be doing a little more than that. For instance, there's 
nothing to say we can't do irrelevant quantification when producing types. So

  .(x : T) -> Set

is a family of types parametric in its index. But, that's probably pretty 
boring. One isomorphism that holds is:

  .(_ : T) -> U ~ Squash T -> U

so the above family of types is constant. Typically, I wouldn't expect people 
to write such animals.

*But*, inductive types package up the relevance modalities. So, when you write 
someting with type:

  F : ∃ A T -> Set

you are (currently) roughly writing:

  F : .(x : A) -> T x -> Set

so irrelevance does show up in types over inductives. I think the most general 
type for the eliminator we can give for ∃ is as follows:

  elim :  (A : Set) -> (T : A -> Set) -> (P : ∃ A T -> Set)
       -> (f : .(x : A) -> (w : T x) -> P (x , w))
       -> (p : ∃ A T) -> P p

which, while it looks strong, will never allow us to write both projections, 
because P is forced to be irrelevant in the first component of the tuple, and 
\(x, _) -> T x is not.

If we look closely, we can see that we are throwing away an opportunity in the 
above type. In

  f : .(x : A) -> (w : T x) -> P (x , w)

the x appears in a type, and so is relevant there. So, that (x , w) could be a 
normal sigma type, instead of ∃. But then p doesn't have that type. So we have 
two options: weaken the rule, as above, or go inside types making more things 
relevant when we're inside irrelevent contexts. The EPTS stuff chooses the 
former, but I can't say that hte latter is wrong (just a lot more 
complicated).

> With first-class irrefutable patterns, we could write
> 
>    snd : forall {A T} -> ((x , w) : exists A T) -> T x
>    snd (x , w) = w
> 
> Seems harmless to me, since it is merely the uncurrying of
> 
>    snd' : forall {A T} -> .(x : A) -> T x -> T x
>    snd' x w = w
> 
> We could analyze the situation as follows: Currently the system is too
> weak to allow free uncurrying (the situation was similar with the Set
> \omega issue in one of your recent mails, but there you could not
> write down the necessary tuple type).

Hmm, yes, this is true, and suspicious.

> > having weak sums is (if you ask me) part of the interest of this stuff.

I thought about this a little, and it occurred to me that weak sums may not 
necessarily be interesting. What is interesting is *abstraction*. That is why 
people working on module systems are intereseted in weak (and translucent) 
sums: they want to enforce abstraction, and weak sums do, by not allowing you 
to project out the first pair of the sum and inspect it.

But, while weak sums are sufficient, I don't know for a fact that they're 
necessary. Perhaps modalities enforcing parametricity are sufficient, even if 
you can program with them similar to strong sums. I'm going to have to ponder 
what exactly 'abstraction' is trying to achieve before I have an answer to 
this, though.

> Yeah, this sounds hacky.
> 
> The problem is that in
> 
> >  p :r ∃ A P
> >  fst p :c A
> >  snd p :r P (fst p)
> 
> p is relevant, but one of its components not, and only after the match
> the irrelevant components surface, so the context reset should happen
> after the matching.

Maybe, but I think it's worse than that, even. Higher-order irrelevant 
function spaces have the same issue. With

  f : (.(x : A) -> P x -> R) -> R

we can envision:

  f konst :c A

for something like 'konst : .(x : A) -> P x -> .A'. Squash actually achieves a 
limited form of this:

  f (const . squash) :r Squash A

Then we've moved the question of higher-order reset to whether Squash A resets 
to (essentially) A, or whether we can match on a Squash and then reset.

> Maybe the problem requires us to introduce modalities.  I have seen
> approaches with an infinite set of binding-times, not just compile,
> and runtime, but I do not know how they relate here.

I think the EPTS stuff may well be viewed as having multiple binding times 
when you take higher-order and inductive stuff into account. I'm not sure if 
that's a good thing or a bad thing.

Inductive types, and Squash in particular, allow you to package up the 
modality as a unary operator. I think that means that introducing them 
primitively should not be necessary, as long as we can figure out how they 
should behave, and make inductive (and higher-order) types behave that way 
instead.

Perhaps Squash isn't even the right name for said data type, and assuming that 
it is is misleading us. For instance, we expect Squash A to have at most one 
element, and so for functions out of it to be constant. But perhaps they 
should only be constant in dynamic contexts, for instance.

As an additional weirdness point, Mishra-Linger notes in his thesis that the 
EPTS Squash is not a monad in the basic theory:

  data Squash (A : Set) : Set where
    squash : .A -> Squash A

  elim :  (A : Set) (P : Squash A -> Set)
       -> (.(x : A) -> P (squash x)) -> (b : Squash A) -> P b

  join : (A : Set) -> Squash (Squash A) -> Squash A
  join = elim (Squash A) (\_ -> Squash A) ?

What to fill in the hole is the issue. It needs to have type

  .(Squash A) -> Squash A

But \x -> x fails for this. We also cannot do tricks with elim, because (at 
best) it will reduce down to writing .A -> A. So, in the default system, 
Squash A is not interchangeable with Squash (Squash A), and so on. He fixes 
this in an extended system, which only checks equality after erasure, by 
adding squashed axioms. For instance:

  flat : Squash (.(A : Set) -> Squash A -> A)

After erasure, all inhabitants of Squash A look like 'squash', so you can add 
axioms of the above form by asserting that 'squash' has the relevant type. The 
above allows you to write join.

*However*, if context reset modifies types, I believe Squash will naturally be 
a monad (even if it may not exactly be a squash type anymore):

  join = elim (Squash A) (\_ -> Squash A)
          (/\sq -> squash (elim A A (\x -> x) sq))

Above, our first elimination gets passed an irrelevant Squash A. Then, we use 
the squash constructor, and need an A. But, the argument to squash is 
irrelevant, so sq should both reset to being relevant *and* being something 
like Id A rather than Squash A. This allows us to eliminate into A 
successfully. This all erases down to:

  join = elim squash

which is approximately (exactly?) what Mishra-Linger's does, as well, without 
the axiom intermediary.

Perhaps this is on the right track?

-- Dan


More information about the Agda mailing list