有没有办法在类型级别打开Maybe
monad中的值?例如,如何为具有此tail
变体的Vec
定义类型安全的pred
:
pred : ℕ -> Maybe ℕ
pred 0 = nothing
pred (suc n) = just n
?像
这样的东西tail : ∀ {n α} {A : Set α} -> Vec A n ->
if isJust (pred n) then Vec A (from-just (pred n)) else ⊤
这个例子完全是人为的,但并不总是可以摆脱一些先决条件,所以你可以用标准库中的tail
函数来构造正确的构造定义:
tail : ∀ {a n} {A : Set a} → Vec A (1 + n) → Vec A n
tail (x ∷ xs) = xs
答案 0 :(得分:7)
我们可以为其定义数据类型:
data _>>=ᵗ_ {α β} {A : Set α} : (mx : Maybe A) -> (A -> Set β) -> Set (α ⊔ β) where
nothingᵗ : ∀ {B} -> nothing >>=ᵗ B
justᵗ : ∀ {B x} -> B x -> just x >>=ᵗ B
即。 mx >>=ᵗ B
是B x
,其中just x ≡ mx
或"没有"。然后,我们可以为tail
定义Vec
,如下所示:
pred : ℕ -> Maybe ℕ
pred 0 = nothing
pred (suc n) = just n
tailᵗ : ∀ {α n} {A : Set α} -> Vec A n -> pred n >>=ᵗ Vec A
tailᵗ [] = nothingᵗ
tailᵗ (x ∷ xs) = justᵗ xs
在[]
案例中,n
为0
,因此pred n
缩减为nothing
,nothingᵗ
是我们唯一可以返回的值
在x ∷ xs
案例中,n
为suc n'
,因此pred n
缩减为just n'
,我们需要将justᵗ
构造函数应用于值Vec A n'
的值,即xs
。
我们可以定义from-justᵗ
,就像在from-just
中定义Data.Maybe.Base
一样:
From-justᵗ : ∀ {α β} {A : Set α} {B : A -> Set β} {mx : Maybe A} -> mx >>=ᵗ B -> Set β
From-justᵗ nothingᵗ = Lift ⊤
From-justᵗ (justᵗ {B} {x} y) = B x
from-justᵗ : ∀ {α β} {A : Set α} {B : A -> Set β} {mx : Maybe A} -> (yᵗ : mx >>=ᵗ B) -> From-justᵗ yᵗ
from-justᵗ nothingᵗ = _
from-justᵗ (justᵗ y) = y
然后实际的tail
函数是
tail : ∀ {n α} {A : Set α} -> (xs : Vec A n) -> From-justᵗ (tailᵗ xs)
tail = from-justᵗ ∘ tailᵗ
一些测试:
test-nil : tail (Vec ℕ 0 ∋ []) ≡ lift tt
test-nil = refl
test-cons : tail (1 ∷ 2 ∷ 3 ∷ []) ≡ 2 ∷ 3 ∷ []
test-cons = refl
但是我们希望能够映射mx >>=ᵗ B
类型的值,所以让我们尝试为其定义一个函数:
_<$>ᵗ_ : ∀ {α β γ} {A : Set α} {B : A -> Set β} {C : ∀ {x} -> B x -> Set γ} {mx : Maybe A}
-> (∀ {x} -> (y : B x) -> C y) -> (yᵗ : mx >>=ᵗ B) -> mx >>=ᵗ λ x -> {!!}
g <$>ᵗ yᵗ = {!!}
如何填补空洞?在第一洞,我们有
Goal: Set (_β_86 yᵗ)
————————————————————————————————————————————————————————————
x : A
yᵗ : mx >>=ᵗ B
mx : Maybe A
C : {x = x₁ : A} → B x₁ → Set γ
B : A → Set β
A : Set α
等式just x ≡ mx
应该成立,但我们无法证明这一点,因此无法将yᵗ : mx >>=ᵗ B
转换为y : B x
,以便填补空白C y
。我们可以通过_<$>ᵗ_
上的模式匹配来定义yᵗ
的类型,但是我们无法使用相同的_<$>ᵗ_
映射已映射的内容。
因此我们需要在mx ≡ just x
中建立mx >>=ᵗ λ x -> e
的属性。我们可以指定_>>=ᵗ_
此类型签名:
data _>>=ᵗ_ {α β} {A : Set α} : (mx : Maybe A) -> (∀ {x} -> mx ≡ just x -> Set β) -> Set (α ⊔ β)
但我们真正关心的是mx
案例中just
为justᵗ
- 如果需要,我们可以恢复x
部分。因此定义:
Is-just : ∀ {α} {A : Set α} -> Maybe A -> Set
Is-just = T ∘ isJust
data _>>=ᵗ_ {α β} {A : Set α} : (mx : Maybe A) -> (Is-just mx -> Set β) -> Set (α ⊔ β) where
nothingᵗ : ∀ {B} -> nothing >>=ᵗ B
justᵗ : ∀ {B x} -> B _ -> just x >>=ᵗ B
我没有使用标准库中的Is-just
,因为它没有计算 - 在这种情况下它至关重要。
但这个定义存在问题:
tailᵗ : ∀ {α n} {A : Set α} -> Vec A n -> pred n >>=ᵗ λ n' -> {!!}
洞中的上下文看起来像
Goal: Set _230
————————————————————————————————————————————————————————————
n' : Is-just (pred n)
A : Set α
n : ℕ
n'
不是数字。可以通过n
上的模式匹配将其转换为数字,但这太冗长和丑陋了。相反,我们可以在辅助函数中包含此模式匹配:
! : ∀ {α β} {A : Set α} {B : ∀ {mx} -> Is-just mx -> Set β} {mx : Maybe A}
-> (∀ x {_ : mx ≡ just x} -> B {just x} _) -> (imx : Is-just mx) -> B imx
! {mx = nothing} f ()
! {mx = just x } f _ = f x {refl}
!
来自一个函数,该函数作用于A
,一个作用于Is-just mx
的函数。 {_ : mx ≡ just x}
部分不是必需的,但拥有此属性可能很有用。
tailᵗ
的定义是
tailᵗ : ∀ {α n} {A : Set α} -> Vec A n -> pred n >>=ᵗ ! λ pn -> Vec A pn
tailᵗ [] = nothingᵗ
tailᵗ (x ∷ xs) = justᵗ xs
from-justᵗ
几乎和以前一样:
From-justᵗ : ∀ {α β} {A : Set α} {mx : Maybe A} {B : Is-just mx -> Set β}
-> mx >>=ᵗ B -> Set β
From-justᵗ nothingᵗ = Lift ⊤
From-justᵗ (justᵗ {B} y) = B _
from-justᵗ : ∀ {α β} {A : Set α} {mx : Maybe A} {B : Is-just mx -> Set β}
-> (yᵗ : mx >>=ᵗ B) -> From-justᵗ yᵗ
from-justᵗ nothingᵗ = _
from-justᵗ (justᵗ y) = y
tail
是一样的:
tail : ∀ {α n} {A : Set α} -> (xs : Vec A n) -> From-justᵗ (tailᵗ xs)
tail = from-justᵗ ∘ tailᵗ
测试通过:
test-nil : tail (Vec ℕ 0 ∋ []) ≡ lift tt
test-nil = refl
test-cons : tail (1 ∷ 2 ∷ 3 ∷ []) ≡ 2 ∷ 3 ∷ []
test-cons = refl
但是现在我们还可以定义一个类似fmap的函数:
runᵗ : ∀ {α β} {A : Set α} {mx : Maybe A} {B : Is-just mx -> Set β}
-> mx >>=ᵗ B -> (imx : Is-just mx) -> B imx
runᵗ {mx = nothing} _ ()
runᵗ {mx = just x} (justᵗ y) _ = y
_<$>ᵗ_ : ∀ {α β γ} {A : Set α} {mx : Maybe A} {B : Is-just mx -> Set β} {C : ∀ {x} -> B x -> Set γ}
-> (∀ {x} -> (y : B x) -> C y) -> (yᵗ : mx >>=ᵗ B) -> mx >>=ᵗ C ∘ runᵗ yᵗ
g <$>ᵗ nothingᵗ = nothingᵗ
g <$>ᵗ justᵗ y = justᵗ (g y)
即。拥有imx : Is-just mx
我们可以使用mx >>=ᵗ B
函数将B imx
缩减为runᵗ
。将C
应用于结果将提供所需的类型签名。
请注意just x
案例
runᵗ {mx = just x} (justᵗ y) _ = y
y : B tt
,而Goal : B imx
。我们可以将B tt
视为B imx
,因为⊤
的所有居民都难以区分,正如
indistinguishable : ∀ (x y : ⊤) -> x ≡ y
indistinguishable _ _ = refl
这是由⊤
数据类型的eta规则引起的。
这是最后的测试:
test : from-justᵗ ((0 ∷_) <$>ᵗ ((0 ∷_) <$>ᵗ tailᵗ (1 ∷ 2 ∷ 3 ∷ []))) ≡ 0 ∷ 0 ∷ 2 ∷ 3 ∷ []
test = refl
我们还可以介绍一些语法糖:
¡ : ∀ {α β} {A : Set α} {B : A -> Set β} {mx : Maybe A}
-> (∀ x {_ : mx ≡ just x} -> B x) -> mx >>=ᵗ ! λ x -> B x
¡ {mx = nothing} f = nothingᵗ
¡ {mx = just x} f = justᵗ (f x {refl})
在不需要统一时使用它,例如:
pred-replicate : ∀ {n} -> pred n >>=ᵗ ! λ pn -> Vec ℕ pn
pred-replicate = ¡ λ pn -> replicate {n = pn} 0
!
或者可以定义为
is-just : ∀ {α} {A : Set α} {mx} {x : A} -> mx ≡ just x -> Is-just mx
is-just refl = _
!' : ∀ {α β} {A : Set α} {mx : Maybe A} {B : Is-just mx -> Set β}
-> (∀ x {p : mx ≡ just x} -> B (is-just p)) -> (imx : Is-just mx) -> B imx
!' {mx = nothing} f ()
!' {mx = just x } f _ = f x {refl}
由于B
现在属于Is-just mx -> Set β
而不是∀ {mx} -> Is-just mx -> Set β
,因此此定义更易于推理,但由于is-just
中存在模式匹配,因此此定义可以可能会打破一些β平等。
¡'
也可以这种方式定义
¡' : ∀ {α β} {A : Set α} {mx : Maybe A} {B : Is-just mx -> Set β}
-> (∀ x {p : mx ≡ just x} -> B (is-just p)) -> mx >>=ᵗ B
¡' {mx = nothing} f = nothingᵗ
¡' {mx = just x} f = justᵗ (f x {refl})
但这个定义打破了所需的β平等:
pred-replicate' : ∀ {n} -> pred n >>=ᵗ ! λ pn -> Vec ℕ pn
pred-replicate' = ¡' λ pn {_} -> {!!}
洞的类型为! (λ pn₁ {._} → Vec ℕ pn₁) (is-just p)
,而不是Vec ℕ pn
。
code。
编辑原来这个版本不太实用。我现在正在使用this:
data _>>=ᵀ_ {α β} {A : Set α} : (mx : Maybe A) -> (∀ x -> mx ≡ just x -> Set β) -> Set β where