[Agda] Unification assumes injective postulates?

Jan Malakhovski oxij at oxij.org
Fri Nov 16 16:53:53 CET 2012


On Wed, 14 Nov 2012 11:03:54 +0100
Andreas Abel <andreas.abel at ifi.lmu.de> wrote:

> The basic question here is how we should understand
> 
>    postulate foo : Nat -> Nat
> 
> I see two basic interpretations here:
> 
> 1. foo is a fixed, arbitrary unknown function.
> 2. foo is a fixed, specific unknown function.
> 
> Interpretation 1, which seems to be the one taken by Agda, allows to 
> reduce foo x == foo y to x == y, since there are non-constant functions. 
>   A postulate behaves like a module parameter or a function parameter 
> (free variable).
> 
> Interpretation 2 is what you seem to expect.  You get this behavior when 
> you write
> 
>    foo : Nat -> Nat
>    foo = ?
> 
> Then, Agda only complains about unsolved constraints.
> 
> One solution for your issue could be that you replace your postulates by 
> holes.  However, that might litter your Agda session with distracting 
> goals.  Another take would be to have a second form of postulate, an 
> "existential postulate" opposed to the current "universal postulate".

Ok, so I carefully thought about this and there are still things I don't understand fully.
Correct me if I'm wrong somewhere.

It looks that
~~~~
postulate foo : ℕ → ℕ

bar : foo zero ≡ foo (succ zero) → foo zero ≡ foo (succ zero)
bar refl = {!!}
~~~~

and

~~~~
module Postulate (foo : ℕ → ℕ) where
  bar : foo zero ≡ foo (succ zero) → foo zero ≡ foo (succ zero)
  bar refl = {!!}
~~~~
work the same way.

So, if I think about postulates as "unknown exported module
parameters" (consider two modules, where the former postulates
something and the latter imports the former) interpretation 1 makes
sense (e.g. for "Level" terms that depend on it should work for any
internal structure of "Level").

Personally, I use module parameters explicitly when I need my proof to
work universally (so does the standard library, except for Level and
primitives).  So, up until now I expected postulates to be "named
holes" (e.g. I think LF-style definition from Dan Licata's message
assumes this interpretation for "if", and I think I can interpret
Level and primitives from this point of view too), but they are not.

I don't quite understand why the "universal interpretation" is the
default, but I wouldn't say that "existential interpretation" should
be the default either, because I don't clearly understand when I
should use which.

Anyway, the fact that changing postulate into a hole changes behavior was
quite unexpected.
I certainly think this should be clearly documented somewhere.

Having an "existential version" of a postulate would also be really
nice, yes.

By the way, abstracting a hole makes it into a "postulate" again
~~~~
abstract
  foo : ℕ → ℕ
  foo = ?

bar : foo zero ≡ foo (succ zero) → foo zero ≡ foo (succ zero)
bar refl = ?
~~~~

On Wed, 14 Nov 2012 18:51:37 +0100
Andreas Abel <andreas.abel at ifi.lmu.de> wrote:

> On 14.11.2012 15:40, Dan Licata wrote:
> > On the other hand, the intuition that
> >
> >    if two neutral terms with the same head are definitionally equal,
> >    then they have the same spine
> >
> > makes sense to me.  Is this the property that justifies the move you're
> > making above?
> >
> > On the other other hand, I can imagine situations where that fails,
> > because e.g. you have eta for coproducts definitionally.
> >
> > So, what is it about definitional equality that justifies that move from
> > foo x == foo y to x == y, and how does Agda know not to apply the same
> > reasoning to propositional equality?
> 
> The unifier called during pattern-checking behaves different than the 
> constraint solver called during equality-checking.  The unifier is based 
> on extensional equality (run-time equality of closed things) while the 
> constraint solver is based on intensional equality (compile-time 
> definitional equality of open things).
> 
> This is a bit subtle, I keep mixing this up myself...

This is really interesting, could you please elaborate on that
a bit more? Is there a term that shows the difference?

Form my naive skin-deep understanding of when pattern-checking and
equality-checking happen they are both existential to a certain degree:

~~~~
dom-easy : (f : ℕ → ℕ) → f ≡ (λ x → f x) → ⊤
dom-easy f refl = _

dom : (f g : ℕ → ℕ) → g ≡ (λ x → f x) → ⊤
dom f .f refl = _

cdom : (f : ℕ → ℕ) → f ≡ (λ x → f x)
cdom f = refl

both : (f g : ℕ → ℕ) → g ≡ (λ x → f x) → f ≡ g
both f .f refl = refl
~~~~

Also, I would explain
> > So, what is it about definitional equality that justifies that move from
> > foo x == foo y to x == y, and how does Agda know not to apply the same
> > reasoning to propositional equality?
to myself as: "It is not. Unification of "foo x == foo y" doesn't
imply x == y, but *requires* them to be equal definitionally."  Am I
wrong? If I'm not then how does it correlate to
extensional-intensional explanation?

Best regards,
  Jan


More information about the Agda mailing list