どっちがいいんだろう?

何かを定義する時に、

∃n > 0.n + n > 0

のように、0より大きなある自然数nを加算した結果は0よりも大きいという意味が無い命題を書きたい時に

∃n > 0

といった、ある種自然数のsubtypeを作って定義を進めたい、というような時、Agda2ではどう書くのが良いんだろうなーというのが在ります。

例えば

module Hatena where

data Nat : Set0 where
  zero : Nat
  succ : Nat -> Nat

three : Nat
three = succ (succ (succ zero))

data Bool : Set0 where
  true : Bool
  false : Bool

not : Bool -> Bool
not true = false
not false = true

data Id {A : Set0} : A -> A -> Set0 where
  refl : (a : A) -> Id a a

record pair (A : Set0) (B : Set0) : Set0 where
  field
    fst : A
    snd : B

record ∃ {A : Set}(P : A -> Set) : Set where
  field
    witness : A
    proof   : P witness

_<_ : Nat -> Nat -> Bool
zero < zero = false
zero < (succ _) = true
(succ a) < zero = false
(succ a) < (succ b) = a < b

isEven : Nat -> Bool
isEven zero = true
isEven (succ zero) = false
isEven (succ (succ a)) = isEven a

isOdd : Nat -> Bool
isOdd a = not (isEven a)

-- p1 : ∃n < 3.isEven(n)
-- どっちが良いだろう?
-- 他にもあるかなー
postulate pr1-1 : ∃ (\n -> Id (n < three) true -> Id (isEven n) true)
postulate pr1-2 : ∃ (\n -> pair (Id (n < three) true) (Id (isEven n) true))

-- pr2-aux : ∀n.isEven(n) -> isOdd(succ n)
postulate pr2-aux : {n : Nat} -> Id (isEven n) true -> Id (isOdd (succ n)) true

-- pr2 : ∃n.isOdd(succ n)
pr2 : ∃ \n -> Id (isOdd (succ n)) true
pr2 = pr2-aux tmp
  where
    w : Nat
    w = ∃.witness pr1-1

    proof : Id (w < three) true -> Id (isEven w) true
    proof = ∃.proof pr1-1

    tmp : Id (isEven w) true
    tmp = proof (refl true) -- ここで失敗

pr2' : ∃ \n -> Id (isOdd (succ n)) true
pr2' = pr2-aux tmp
  where
    w : Nat
    w = ∃.witness pr1-2

    proof : pair (Id (w < three) true) (Id (isEven w) true)
    proof = ∃.proof pr1-2

    tmp : Id (isEven w) true
    tmp = pair.snd proof

pr2は明ですが、まぁそれを何故か

pr2-aux : ∀n.isEven(n) -> isOdd(succ n)
と
p1 : ∃n < 3.isEven(n)

を使って証明します。

でその時に、∃のAgda2での表現はまぁどうするのが適当なんだろうなーという事です。

ちなみに、上記定義であればpr2だと型チェックで失敗。pr2'だと成功です。

(敢えてpostulateを使っているのでpr2だとこの場合は失敗するのですが(というのは、witnessなconcreteな値を取れないのでtmpで失敗))

追記

ATS(Applied Type System)では割と上手く振る舞ってくれるというのを噂で聞いたのですが、ATSはようわからん...

こっちの方が良いかも

module Filter where

data _==_ {A : Set0} : A -> A -> Set0 where
  refl : (a : A) -> a == a

symm-I : {A : Set0}{a b : A} -> a == b -> b == a
symm-I (refl a) = refl a

trans-I : {A : Set0}{a b c : A} -> a == b -> b == c -> a == c
trans-I (refl a) bc = bc

data Nat : Set0 where
  zero : Nat
  succ : Nat -> Nat

data Array (A : Set0) : Nat -> Set0 where
  [] : Array A zero
  _::_ : {n : Nat} -> A -> Array A n -> Array A (succ n)

data Bool : Set0 where
  true : Bool
  false : Bool

_<_ : Nat -> Nat -> Bool
zero < zero = false
zero < (succ _) = true
(succ _) < zero = false
(succ a) < (succ b) = a < b

_<=_ : Nat -> Nat -> Bool
zero <= _ = true
(succ _) <= zero = false
(succ a) <= (succ b) = a <= b

<=-lem : {n1 : Nat}{n2 : Nat} -> (n1 <= n2) == true -> (n1 <= (succ n2)) == true
<=-lem {zero}{_} _ = refl true
<=-lem {succ a}{zero} ()
<=-lem {succ a}{succ b} pr = proof
  where
    pr' : (a <= b) == true
    pr' = pr

    hypo1 : (a <= (succ b)) == true
    hypo1 = <=-lem pr'

    hypo2 : (succ a <= (succ (succ b))) == (a <= (succ b))
    hypo2 = refl (a <= (succ b))

    proof : (succ a <= (succ (succ b))) == true
    proof = trans-I hypo2 hypo1

record ∃ {A : Set0} (P : A -> Set0) : Set0 where
  field
    witness : A
    proof   : P witness

record pair (A B : Set0) : Set0 where
  field
    fst : A
    snd : B

-- filter : ∀n.∀A.(pred : A -> Bool) -> Array A n -> ∃n'<=n.Array A n'
{-
filter : {n : Nat} {A : Set0} -> (p : A -> Bool) -> Array A n -> (∃ \n' -> (n' <= n) == true -> Array A n')
filter {zero} {A} p [] = proof
  where
    w : Nat
    w = zero

    pr : (w <= zero) == true -> Array A w
    pr = (\pr -> [])

    proof : ∃ \n' -> (n' <= zero) == true -> Array A n'
    proof = record { witness = w; proof = pr}
filter {succ n} {A} p (x :: xs) with p x
... | true = _
  where
    hypo : ∃ \n' -> (n' <= n) == true -> Array A n'
    hypo = filter p xs
-}

filter' : {n : Nat} {A : Set0} -> (p : A -> Bool) -> Array A n -> (∃ \n' -> pair ((n' <= n) == true) (Array A n'))
filter' {zero} {A} p [] = proof
  where
    w : Nat
    w = zero

    proof : ∃(\n' -> pair ((n' <= zero) == true) (Array A n'))
    proof = record { witness = w; proof = record { fst = refl true ; snd = [] }}
filter' {succ n} {A} p (x :: xs) with p x
... | true = proof
  where
    hypo : ∃(\n' -> pair ((n' <= n) == true) (Array A n'))
    hypo = filter' p xs

    w : Nat
    w = ∃.witness hypo

    l : (w <= n) == true
    l = pair.fst (∃.proof hypo)

    r : Array A w
    r = pair.snd (∃.proof hypo)

    succ_w : Nat
    succ_w = succ w

    aux : (succ w <= succ n) == (w <= n)
    aux = refl (w <= n)

    l' : (succ_w <= (succ n)) == true
    l' = trans-I aux l

    r' : Array A succ_w
    r' = x :: r

    proof : (∃ (\n' -> pair ((n' <= (succ n)) == true) (Array A n')))
    proof = record {witness = succ_w ; proof = record {fst = l' ; snd = r'}}
... | false = proof
  where
    hypo : (∃ (\n' -> pair ((n' <= n) == true) (Array A n')))
    hypo = filter' p xs

    w : Nat
    w = ∃.witness hypo

    l : (w <= n) == true
    l = pair.fst (∃.proof hypo)

    r : Array A w
    r = pair.snd (∃.proof hypo)

    l' : (w <= (succ n)) == true
    l' = <=-lem l

    proof : (∃ (\n' -> pair ((n' <= (succ n)) == true) (Array A n')))
    proof = record {witness = w ; proof = record {fst = l' ; snd = r}}

長さ情報を持つ配列に関するfilterでやってみました。

証明してるのは単に、filter関数は引数で取った配列は、長さだけに注目すると、同じか、それよりも短い配列を返しますよね?ということです。
(配列の内容に関する証明は行ってません。