agda / agda

Agda is a dependently typed programming language / interactive theorem prover.

Home Page:https://wiki.portal.chalmers.se/agda/pmwiki.php

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

with-abstraction regression

UlfNorell opened this issue · comments

The following works in agda-2.6.4.3 but fails on master.

open import Agda.Builtin.Nat
open import Agda.Builtin.Bool
open import Agda.Builtin.Equality

postulate
  IsPreorder : {A : Set}  (A  A  Set)  (A  A  Set)  Set₁

module NatOrder where
  postulate
    _≤_ : Nat  Nat  Set
    ≤-isPreorder : IsPreorder _≡_ _≤_

module BoolOrder where
  postulate
    _≤_ : Bool  Bool  Set
    ≤-isPreorder : IsPreorder _≡_ _≤_

record HasPreorder (A : Set) (_≈_ : A  A  Set) : Set₁ where
  infix 4 _≤_
  field
    _≤_          : A  A  Set
    ≤-isPreorder : IsPreorder _≈_ _≤_

open HasPreorder ⦃...⦄ public

_∋_ :  {a} (A : Set a)  A  A
A ∋ x = x

instance
  Nat-hasPreorder  = HasPreorder _ _ ∋ record {NatOrder}
  Bool-hasPreorder = HasPreorder _ _ ∋ record {BoolOrder}

data Dec (P : Set) : Set where
  yes : P  Dec P
  no : Dec P

case_of_ :  {A B : Set}  A  (A  B)  B
case x of f = f x

record _⁇ (P : Set) : Set where
  constructor ⁇_
  field dec : Dec P

open _⁇ ⦃...⦄ public

¿_¿ :  (X : Set)  ⦃ X ⁇ ⦄  Dec X
¿ _ ¿ = dec

instance
  postulate Dec≤ :  {n m : Nat}  (n ≤ m) ⁇

top : Set
top = Nat
  where  -- only fails in a `where` block
    IsGood : (new : Nat)  Set
    IsGood new = 1 ≤ new

    -- case/with over this works:
    -- checkGood :  new  Dec (IsGood new)
    -- checkGood new = ¿ IsGood new ¿

    foo :  new  Bool
    foo new = case ¿ IsGood new ¿ of λ where
      (yes _)  true
      no  true

    bar :  new  foo new ≡ true
    bar new with ¿ IsGood new ¿   -- with-abstraction fails
    ... | yes _ = refl
    ... | no    = refl

Bisection blames 403ee42.

This is PR (CC @plt-amy)

@plt-amy do you have some time to take a look at this?

What's interesting is that it works if you give the type argument to the (unused) Bool instance:

 Bool-hasPreorder = HasPreorder Bool _ ∋ record {BoolOrder}

The only difference in this case is that we add the instance to the discrimination tree with the Bool directly:

added value Bool-hasPreorder to discrimination tree with case
  case 0 of
    HasPreorder² → case 0 of
      Bool⁰ → done {Bool-hasPreorder}

instead of first adding it as

added value Bool-hasPreorder to discrimination tree with case
  case 0 of
    HasPreorder² → done {Bool-hasPreorder}

and only adding the refined version once the meta is solved. I haven't been able to figure out yet why this makes a difference.

Sure, I'll take a look.

@UlfNorell My immediate guess was right: this is a result of my rather silly attempts to avoid repeatedly waking up instances. The intent was to block on the metas that caused the discrimination tree to go off and find multiple instances:

instance candidates from signature for goal:
  HasPreorder Nat __≈__52
  {Nat-hasPreorder, Bool-hasPreorder} length: 2
blocker:
  __≈__52
mutual block:
  {}

As you can tell from the amount of Bools, this isn't exactly stellar code; turns out many things are sensitive to the order that instance constraints are solved in (like termination checking). Computing the type of a with isn't one of the situations I accounted for. Fixes I can see:

  • I can't tell you whether this code buys us any significant performance (though I'd wager it's an improvement, since it avoids the expensive checkCandidates on average), so I'd be okay with getting rid of it. (i.e. delete InstanceArguments.hs lines 212-269)
  • The alternative is to call wakeupConstraints_ somewhere between inferring the type of the with-scrutinee and computing the abstraction; the postpone-on-overlap doesn't fire when under wakeupConstraints_. I don't know enough about the LHS checker to know where this would go, but I could have a poke.

Thanks @plt-amy! I'm not sure if it's relevant, but I did some waking of instance constraints for with-abstraction in #6868.