【问题标题】:Easy Syntactic Equality in IdrisIdris 中的简单句法等式
【发布时间】:2023-06-03 17:14:01
【问题描述】:

是否有一种简单的方法可以为数据类型编写相等 (DecEq) 实例?例如,我希望在其 DecEq 声明中包含 O(n) 行,其中 ?p 很简单:

data Foo = A | B | C | D

instance [syntactic] DecEq Foo where
   decEq A A = Yes Refl
   decEq B B = Yes Refl
   decEq C C = Yes Refl
   decEq D D = Yes Refl
   decEq _ _ = No ?p

【问题讨论】:

    标签: idris


    【解决方案1】:

    大卫·克里斯蒂安森(David Christiansen)正在努力实现总体上的自动化,他基本上已经完成了;它可以在his GitHub repository 中找到。同时,在这种情况下,这里有一种方法可以将您从 O(n^2) 案例转换为 O(n) 案例。首先,一些预备知识。如果你有一个可判定相等的东西,并且你有一个从你选择的类型到那个类型的注入,那么你可以为那个类型制定一个决策过程:

    IsInjection : (a -> b) -> Type
    IsInjection {a} f = (x,y : a) -> f x = f y -> x = y
    
    decEqInj : DecEq d => (tToDec : t -> d) ->
                          (isInj : IsInjection tToDec) ->
                          (p, q : t) -> Dec (p = q)
    decEqInj tToDec isInj p q with (decEq (tToDec p) (tToDec q))
      | (Yes prf) = Yes (isInj p q prf) 
      | (No contra) = No (\pq => contra (cong pq)) 
    

    不幸的是,直接证明你的函数是一个注入会使你回到 O(n^2) 的情况,但通常情况下,任何具有撤回的函数都是单射的:

    retrInj : (f : d -> t) -> (g : t -> d) ->
              ((x : t) -> f (g x) = x) ->
              IsInjection g
    retrInj f g prf x y gxgy =
      let fgxfgy = cong {f} gxgy
          foo = sym $ prf x
          bar = prf y
      in trans foo (trans fgxfgy bar)
    

    因此,如果您具有从您选择的类型到具有可判定相等性的函数并为其撤回,那么您的类型具有可判定相等性:

    decEqRet : DecEq d => (decToT : d -> t) ->
               (tToDec : t -> d) ->
               (isRet : (x : t) -> decToT (tToDec x) = x) ->
               (p, q : t) -> Dec (p = q)
    decEqRet decToT tToDec isRet p q =
      decEqInj tToDec (retrInj decToT tToDec isRet) p q
    

    最后,您可以根据自己的选择编写案例:

    data Foo = A | B | C | D
    
    natToFoo : Nat -> Foo
    natToFoo Z = A
    natToFoo (S Z) = B
    natToFoo (S (S Z)) = C
    natToFoo _ = D
    
    fooToNat : Foo -> Nat 
    fooToNat A = 0
    fooToNat B = 1
    fooToNat C = 2
    fooToNat D = 3
    
    fooNatFoo : (x : Foo) -> natToFoo (fooToNat x) = x
    fooNatFoo A = Refl
    fooNatFoo B = Refl
    fooNatFoo C = Refl
    fooNatFoo D = Refl
    
    instance DecEq Foo where
      decEq x y = decEqRet natToFoo fooToNat fooNatFoo x y
    

    请注意,虽然natToFoo 函数有一些较大的模式,但实际上并没有太多动作。应该可以通过嵌套使模式变小,尽管这可能很难看。

    泛化:起初我认为这仅适用于特殊情况,但我现在认为它可能比这要好一些。特别是,如果您有一个代数数据类型持有具有可判定相等性的类型,您应该能够将其转换为嵌套Pair 的嵌套Either,这将使您到达那里。例如(使用Maybe缩短Either (Bool, Nat) ()):

    data Fish = Cod Int | Trout Bool Nat | Flounder
    
    watToFish : Either Int (Maybe (Bool, Nat)) -> Fish
    watToFish (Left x) = Cod x
    watToFish (Right Nothing) = Flounder
    watToFish (Right (Just (a, b))) = Trout a b
    
    fishToWat : Fish -> Either Int (Maybe (Bool, Nat))
    fishToWat (Cod x) = Left x
    fishToWat (Trout x k) = Right (Just (x, k))
    fishToWat Flounder = Right Nothing
    
    fishWatFish : (x : Fish) -> watToFish (fishToWat x) = x
    fishWatFish (Cod x) = Refl
    fishWatFish (Trout x k) = Refl
    fishWatFish Flounder = Refl
    
    instance DecEq Fish where
      decEq x y = decEqRet watToFish fishToWat fishWatFish x y
    

    【讨论】: