Устранение возможного на уровне типа

Есть ли способ развернуть значение, которое находится внутри монады Maybe, на уровне типа? Например, как определить безопасный тип 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

person user3237465    schedule 28.06.2015    source источник


Ответы (1)


Первая попытка

Мы можем определить тип данных для этого:

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 для Vecs следующим образом:

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.


код.


EDIT Оказалось, что эта версия не очень удобна. Сейчас я использую это:

data _>>=ᵀ_ {α β} {A : Set α} : (mx : Maybe A) -> (∀ x -> mx ≡ just x -> Set β) -> Set β where
person user3237465    schedule 28.06.2015