[Agda] Computation and patterns in called functions

Francesco Mazzoli f at mazzo.li
Mon Jan 28 13:35:17 CET 2013

Hi list,

I frequently encounter a nasty problem when having to prove properties about
functions with that pattern match only on certain constructors, since in the
proof I have to consider every possible case for the terms to compute.  I think
that an heuristic that checks whether the matched constructors have been matched
already and takes the last branch otherwise would be very useful - or maybe
there is already a solution that I’m not aware of.

The issue is better explained with an example (in this case taken from CPDT):

    Var : Set
    Var = ℕ

    Vars : Set
    Vars = Var → Var

    data Exp : Set where
      const : ℕ         → Exp
      var   : Var       → Exp
      plus  : Exp → Exp → Exp

    evalExp : Vars → Exp → ℕ
    evalExp vs (const n)    = n
    evalExp vs (var v)      = vs v
    evalExp vs (plus e₁ e₂) = evalExp vs e₁ + evalExp vs e₂

    optExp : Exp → Exp
    optExp (plus (const 0) e) = optExp e
    optExp (plus e₁ e₂)       = plus (optExp e₁) (optExp e₂)
    optExp e                  = e

Now, we predictably we want to prove that the optimisation preserves the value
of the expression:

    optExp-correct : ∀ {vs} e → evalExp vs e ≡ evalExp vs (optExp e)
    optExp-correct (const n) = refl
    optExp-correct (var v) = refl
    optExp-correct (plus (const 0) e) = optExp-correct e
    optExp-correct {vs} (plus (const (suc n)) e) =
      cong₂ _+_ (optExp-correct {vs} (const (suc n))) (optExp-correct e)
    optExp-correct {vs} (plus (var v) e) =
      cong₂ _+_ (optExp-correct {vs} (var v)) (optExp-correct e)
    optExp-correct {vs} (plus (plus e₁ e₂) e₃) =
      cong₂ _+_ (optExp-correct {vs} (plus e₁ e₂)) (optExp-correct e₃)

This is very ugly, since after we have matched the ‘plus (const 0) e’ we know
that the ‘plus e₁ e₂’ branch in ‘optExp’ will be taken.  I don’t have an
implementation in mind but I don’t think it would be too hard to have Agda
understand this so that we can simply write

    optExp-correct : ∀ {vs} e → evalExp vs e ≡ evalExp vs (optExp e)
    optExp-correct (plus (const 0) e) = optExp-correct e
    optExp-correct {vs} (plus e₁ e₂) =
      cong₂ _+_ (optExp-correct {vs} e₁) (optExp-correct e₂)
    optExp-correct e = refl

This problem can often be avoided by pairing the proof with the program:

    optExp′ : (e : Exp) → ∃ (λ e′ → ∀ {vs} → evalExp vs e′ ≡ evalExp vs e)
    optExp′ (plus (const 0) e) = let (e′ , p ) = optExp′ e in e′ , p
    optExp′ (plus e₁ e₂)       = let (e₁′ , p) = optExp′ e₁
                                     (e₂′ , q) = optExp′ e₂
                                 in plus e₁′ e₂′ , cong₂ _+_ p q
    optExp′ e                  = e , refl

However, I don’t think this is satisfactory since the programs themselves will
be much less readable if intertwined with the proofs.

Maybe in this case there is an easier path that I haven’t seen, but I think that
in any case the addition I described would be very useful.


More information about the Agda mailing list