Data.AVL的Functor实例

时间:2014-01-26 21:48:58

标签: parameters functor agda

我想为Data.AVL.Indexed.Tree定义一个仿函数实例。这似乎很棘手,因为存储树的上限和下限的索引的类型Key⁺“依赖于”树中值的类型(或类型族)。

我想要做的是在下面实施fmap

open import Relation.Binary
open import Relation.Binary.PropositionalEquality

module Temp
   {k ℓ}
   {Key : Set k}
   {_<_ : Rel Key ℓ}
   (isStrictTotalOrder : IsStrictTotalOrder _≡_ _<_) where

open import Function

module AVL (Value : Set)
   where open import Data.AVL (const Value) isStrictTotalOrder public

open AVL

fmap : {A B : Set} → (A → B) → ∀ {l u h} → 
       Indexed.Tree A l u h → Indexed.Tree B {!!} {!!} {!!}
fmap f = {!!}

现在我忽略了依赖类型的树,而是假设值具有常量类型。我们的想法是创建AVL模块的本地变体,仅在类型Value上进行参数化,并在不同类型实例化,以便给出fmap的签名。

问题是,我似乎无法以这样的方式量化lu,我可以通过相同的界限到Indexed.Tree的两个不同实例。我想我明白为什么会这样:有两种不同的Key⁺类型,一种用于Indexed.Tree A,另一种用于Indexed.Tree B。但我希望这些类型是相同的。

我是否遗漏了一些明显的内容,或Data.AVL只是没有参数设置允许我定义fmap

1 个答案:

答案 0 :(得分:2)

你在这里没有遗漏任何明显的东西。尽管Key⁺忽略了Value参数,但Agda仍然将它们视为不同的类型。但是,您可以非常轻松地编写转换函数。我将打开一些模块以保持名称可以忍受:

open Extended-key
open Height-invariants

open import Data.Nat

移动Key⁺非常简单,因为基础Key是相同的:

to : ∀ {A B} → Key⁺ A → Key⁺ B
to ⊥⁺    = ⊥⁺
to ⊤⁺    = ⊤⁺
to [ k ] = [ k ]

移动_<⁺_关系也是可行的,您只需要在Key⁺上进行模式匹配,以便将类型简化为(基本上)标识:

to< : ∀ {A B} l u → _<⁺_ A l u → _<⁺_ B (to l) (to u)
to< ⊥⁺   ⊥⁺     p = p
to< ⊥⁺   ⊤⁺     p = p
to< ⊥⁺   [ _ ]  p = p
to< ⊤⁺    _     p = p
to< [ _ ] ⊥⁺    p = p
to< [ _ ] ⊤⁺    p = p
to< [ _ ] [ _ ] p = p

现在,看起来这应该可以解决这个问题,但是当你试图把它写下来时,你很快就会发现你需要一种方法来运输平衡。再一次,没什么特别的:

to∼ : ∀ {A B h₁ h₂} → _∼_ A h₁ h₂ → _∼_ B h₁ h₂
to∼ ∼+ = ∼+
to∼ ∼0 = ∼0
to∼ ∼- = ∼-

fmap的类型如下所示:

fmap : ∀ {A B} (f : ∀ {x} → A x → B x) {l u h} →
       Indexed.Tree A l u h → Indexed.Tree B (to l) (to u) h

这不是一个“真正的”fmap,但考虑到你需要有不同的Key⁺,这是我们能够得到的。

leaf案例需要使用to<,但其他方面却是微不足道的:

fmap f {lb} {ub} (Indexed.leaf l<u) = Indexed.leaf (to< lb ub l<u)

node案例是事情变得有趣的地方。显而易见的解决方案:

fmap f (Indexed.node (k , v) l r bal) =
  Indexed.node (k , f v) (fmap f l) (fmap f r) (to∼ bal)

没有进行类型检查。让我们找出原因。以下是上述表达式的类型和目标类型:

-- Have:
Indexed.Tree .B (to .l) (to .u) (suc (max .B (to∼ bal)))

-- Goal:
Indexed.Tree .B (to .l) (to .u) (suc (max .A bal))

所以我们需要一个额外的证明:

max≡ : ∀ {A B} {h₁ h₂} (bal : _∼_ A h₁ h₂) →
  max A bal ≡ max B (to∼ bal)
max≡ ∼+ = refl
max≡ ∼0 = refl
max≡ ∼- = refl

最后,我们可以通过max≡ bal重写目标类型并获得所需的实现:

fmap f (Indexed.node (k , v) l r bal) rewrite max≡ bal =
  Indexed.node (k , f v) (fmap f l) (fmap f r) (to∼ bal)

我也在使用树的更依赖变体。这是通过将AVL模块定义为:

来完成的
import Data.AVL
module AVL (Value : Key → Set)
  = Data.AVL Value isStrictTotalOrder

然后只需要映射函数来尊重Key值:

mapping-function : {A B : Key → Set} {x : Key} → A x → B x

还有另一种选择:重写Data.AVL模块,Extended-keyHeight-invariants不会被Value参数化。虽然这需要更改标准库,但我认为这是更好的解决方案。 Extended-keyHeigh-invariantsData.AVL之外肯定有用 - 事实上,我有一个Data.BTree正好使用它。

如果您决定采用这种方式,请考虑将它们分成新模块(例如Data.ExtendedKey)并提交补丁/拉取请求。