Search code examples
functional-programmingagdaagda-stdlib

Ill-typed with abstraction while pattern matching on the decideable comparator, union type and dependent pair agda


I have an object, which is represented as a function L → A ⊎ B. I usually denote it as N. It is some mapping from label l to either A or B.

I have two types of reductions (in my example reductionType1 and reductionType2) and a mapping between them, so that we could produce the second reduction from the first one.

These reductions work only for the A type.

In my project I want to apply these reductions on particular object, replacing the result for special label with result of reduction.

ReductionType1 only applied in singular manner, while ReductionType2 is applied in "parallel manner".

I have a function red2FromRed1SingleReductionMap, which constructs parallel reductions of type 2 from a single reduction type 1.

And I have functions reductionMapsTypeExample singleReduct1N which apply these reductions to particular N.

The problem that I face with is somehow related to "ill-typed with abstractions", and I do not know how to overcome it :(

Here is the example, that I tried to construct, which represents my logic (I really tried to make it simple...)

open import Relation.Binary.PropositionalEquality using (_≡_)
open import Data.Sum.Base using (_⊎_; inj₁; inj₂; [_,_]′)

open import Agda.Builtin.Sigma using (Σ; _,_; fst; snd)
open import Agda.Builtin.Unit using (⊤; tt)

open import Relation.Nullary
open import Relation.Nullary.Decidable
open import Relation.Binary.Core

data L : Set where
  x : L
  y : L
data A : Set where
  a1 : A
  a2 : A
data B : Set where
  b1 : B
  b2 : B

data reductionType1 : A → A → Set where
  reductoinRule11 : reductionType1 a1 a1
  reductoinRule12 : reductionType1 a2 a2

data reductionType2 : A → A → Set where
  reductoinRule21 : reductionType2 a1 a1
  reductoinRule22 : reductionType2 a2 a2

mapReduction1ToReduction2 : ∀ {a a'} → reductionType1 a a' → reductionType2 a a'
mapReduction1ToReduction2 reductoinRule11 = reductoinRule21
mapReduction1ToReduction2 reductoinRule12 = reductoinRule22

reduction2-reflexive : ∀ {a} → reductionType2 a a
reduction2-reflexive {a1} = reductoinRule21
reduction2-reflexive {a2} = reductoinRule22

reductionMapsTypeExample : (N : L → A ⊎ B) → (c : L) → Set
reductionMapsTypeExample N l with N l
... | (inj₁ a) = Σ A (reductionType2 a)
... | (inj₂ b) = ⊤

parallelReductNExample : 
  (N : L → A ⊎ B)
  → (reductions : (l : L) → reductionMapsTypeExample N l)
  → (L → A ⊎ B)
parallelReductNExample N reductions l with N l | reductions l
... | (inj₁ a) | (a' , _) = inj₁ a'
... | (inj₂ b) | _ = inj₂ b

red2FromRed1SingleReductionMap : (comp : (l : L) → (l' : L) → Dec (l ≡ l'))
  → (N : L → A ⊎ B) 
  → (specialL : L) 
  → (a a' : A) 
  → (e : (N specialL) ≡ (inj₁ a)) 
  → (red1 : reductionType1 a a') 
  → ((l : L) → reductionMapsTypeExample N l)
red2FromRed1SingleReductionMap comp N specialL a a' e red1 l with comp l specialL
... | yes e_l_specialL with l | e_l_specialL
...   | specialL | _≡_.refl with N specialL | e
...     | inj₁ a | _≡_.refl = a' , (mapReduction1ToReduction2 red1)
red2FromRed1SingleReductionMap comp N specialL a a' e red1 l
    | no _ with N l
...   | inj₁ aa = aa , reduction2-reflexive
...   | inj₂ b = tt

singleReduct1N : ∀ {a a' : A} {comp : (l : L) → (l' : L) → Dec (l ≡ l')}
  → (specialL : L)
  → (N : L → A ⊎ B) 
  → (N specialL ≡ inj₁ a) → reductionType1 a a'
  → (L → A ⊎ B)
singleReduct1N {a} {a'} {comp} specialL N _ _ l with comp l specialL
... | yes _ = inj₁ a'
... | no _ = N l

equalityOfTheseReductions : ∀ {specialL}
  {N : L → A ⊎ B}
  {a a' : A}
  {e : N specialL ≡ (inj₁ a)}
  {red1 : reductionType1 a a'}
  {comp : (l : L) → (l' : L) → Dec (l ≡ l')}
  {l : L}
  → (((parallelReductNExample N (red2FromRed1SingleReductionMap comp N specialL a a' e red1)) l)
    ≡ ((singleReduct1N {a} {a'} {comp} specialL N e red1) l))
equalityOfTheseReductions {specialL} {N} {a} {a'} {e} {red1} {comp} {l} with comp l specialL
... | yes e_l_specialL with l | e_l_specialL
...   | specialL | _≡_.refl with N specialL | red2FromRed1SingleReductionMap comp N specialL a a' e red1 specialL
...     | inj₁ aa | aaa , _ = ?

And the produced error:

N specialL != w₁ of type A ⊎ B
when checking that the type
(specialL : L) {comp : (l l' : L) → Dec (l ≡ l')} {N : L → A ⊎ B}
{a a' : A} {e : N specialL ≡ inj₁ a} {red1 : reductionType1 a a'}
(w₁ : A ⊎ B) →
reductionMapsTypeExample N specialL | w₁ →
{l : L} (e_l_specialL : l ≡ specialL) →
(parallelReductNExample N
 (λ l₁ →
    red2FromRed1SingleReductionMap comp N specialL a a' e red1 l₁
    | comp l₁ specialL)
 specialL
 | w₁
 | (UntypedPhiCalculus.with-1294 specialL specialL _≡_.refl specialL
    _≡_.refl comp N a a' e red1
    | w₁ | e))
≡ inj₁ a'
of the generated with function is well-formed
(https://agda.readthedocs.io/en/v2.6.3/language/with-abstraction.html#ill-typed-with-abstractions)

Solution

  • Indeed, this kind of error is/can be infuriating, because it is just over the horizon of intelligibility for most people (complex machine-generated error message, associated with complex machine-generated expression that the user never themselves wrote), even when your instincts are on the right lines:

    your goal(s) explicitly quantify over an equation which specifies the value and type of a with-abstracted term, which you nevertheless want to correlate 'in-place' with the associated use of with...

    ... so the 'standard'/'official' remedy is to use the new with exp in eqn syntax, which in a given partial construction installs:

    • not only the expression exp which you wish to with-abstract;
    • but also an additional equational hypothesis eqn which asserts the equality of exp and the (various) values of the subsequent pattern scrutinee introduced by the with

    In each of the subsequent case branches corresponding to pattern-matching on the scrutinee, the equation may then (sometimes/usually) be used to rewrite the current goal (in a well-typed way) to 'the thing you want'. See if this helps!

    Sadly, there are examples in the wild where even this desirable outcome fails to hold, and where other methods seem necessary. If your example turns out to be one of those, the Agda stdlib maintainers would like to know, as we have recently deprecated one of the other ways of dealing with with-abstractions in favour of with ... in ... syntax, so failing regression tests are (sadly) useful to know about.