Search code examples
agda

Instance search fails with multiple solutions even though all solutions are equal


Instance search requires that all found solutions are unique. According to the Agda wiki (https://agda.readthedocs.io/en/v2.6.0.1/language/instance-arguments.html, bottom of the page),

From the previous stage we get a list of potential solutions. If the list is empty we fail with an error saying that no instance for C vs could be found (no). If there is a single solution we use it to solve the goal (yes), and if there are multiple solutions we check if they are all equal. If they are, we solve the goal with one of them (yes), but if they are not, we postpone instance resolution (maybe), hoping that some of the maybes will turn into nos once we know more about the involved metavariables.

Here I have some code that uses rose trees of bools. There are two data structures Trues and Falses that witness that the contents of a rose tree is all true or all false, respectively. There is also a function Opp that swaps all trues to falses and vice-versa. Naturally, Trues x -> Falses (Opp x) and vice-versa, so I wrote a function OppTF to witness this. However, Agda now complains that there are two ways to prove Falses (B []), either directly with the constructor for Falses, or via the function OppTF. Of course, both of these produce the same result, and I prove this with the uniq function that proves that if p : Trues (B []) then p is unique. Nevertheless, Agda does not realise this.

How can I convince Agda that all solutions are equal? Unfortunately I cannot make the proof irrelevant with .{{ _ : Falses x }} as I need it later on. So I need to do one of the following:

  • Coax Agda into fully normalising both candidate solutions, which will reveal they are equal.
  • Use the uniq proof to convince Agda that all solutions are equal.
  • Make Agda stop caring about duplicate solutions, without making the argument irrelevant.

Is it possible to do any of these, and if so, how?

Code and error below:

module Example where

open import Data.List
open import Data.Bool
open import Relation.Binary.PropositionalEquality

data Rose : Set where
    V : Bool → Rose
    B : List Rose → Rose

Opp : Rose → Rose
Opp' : List Rose → List Rose

Opp (V x) = V (not x)
Opp (B xs) = B (Opp' xs)

Opp' [] = []
Opp' (x ∷ xs) = Opp x ∷ Opp' xs

data Trues : Rose → Set
data Trues' : List Rose → Set

data Trues where
    instance VTrues : Trues (V true)
    instance TTrues : ∀ {ts} ⦃ _ : Trues' ts ⦄ → Trues (B ts)
    
data Trues' where
    instance VTrues' : Trues' []
    instance TTrues' : ∀ {t ts} ⦃ _ : Trues t ⦄ ⦃ _ : Trues' ts ⦄ → Trues' (t ∷ ts)

data Falses : Rose → Set
data Falses' : List Rose → Set

data Falses where
    instance VFalses : Falses (V false)
    instance TFalses : ∀ {ts} ⦃ _ : Falses' ts ⦄ → Falses (B ts)
    
data Falses' where
    instance VFalses' : Falses' []
    instance TFalses' : ∀ {t ts} ⦃ _ : Falses t ⦄ ⦃ _ : Falses' ts ⦄ → Falses' (t ∷ ts)

instance
    OppTF : ∀ {x} ⦃ _ : Trues x ⦄ → Falses (Opp x)
    OppTF' : ∀ {xs} ⦃ _ : Trues' xs ⦄ → Falses' (Opp' xs)

    OppTF {x} ⦃ VTrues ⦄ = VFalses
    OppTF {x} ⦃ TTrues ⦃ xs ⦄ ⦄ = TFalses ⦃ OppTF' ⦃ xs ⦄ ⦄

    OppTF' {[]} ⦃ VTrues' ⦄ = VFalses'
    OppTF' {x ∷ xs} ⦃ TTrues' ⦃ p ⦄ ⦃ ps ⦄ ⦄ = TFalses' ⦃ OppTF ⦃ p ⦄ ⦄ ⦃ OppTF' ⦃ ps ⦄ ⦄

data Str : Rose → Set where
    Tor : ∀ {x : Rose} → Str x

dummy : ∀ {x : Rose} ⦃ _ : Falses x ⦄ → Rose
dummy {x} = x

test : Rose
test = dummy {B []}

uniq : ∀ {p : Falses (B [])} → p ≡ TFalses ⦃ VFalses' ⦄
uniq {TFalses {_} ⦃ VFalses' ⦄} = refl

Error:

Failed to solve the following constraints:
  Resolve instance argument _70 : Falses (B [])
  Candidates
    TFalses : {ts : List Rose} ⦃ _ : Falses' ts ⦄ → Falses (B ts)
    OppTF : {x : Rose} ⦃ _ : Trues x ⦄ → Falses (Opp x)
    (stuck)

Solution

  • This was solved over on the Zulip chat with the option

    {-# OPTIONS --overlapping-instances #-}

    In this particular case, allowing overlapping instances doesn't cause a slowdown, however in my actual use-case it did, so also adding

    {-# OPTIONS --overlapping-instances --instance-search-depth 10 #-}

    greatly sped it up (the default search depth is 500).