Agda: упрощение рекурсивных определений с использованием Thunk

Я пытаюсь реализовать тип, который представляет (возможно) бесконечный путь в бесконечном двоичном дереве. Определение в настоящее время напоминает определение Conat в stdlib.

open import Size
open import Codata.Thunk

data BinaryTreePath (i : Size) : Set where
  here : BinaryTreePath i
  branchL : Thunk BinaryTreePath i → BinaryTreePath i
  branchR : Thunk BinaryTreePath i → BinaryTreePath i

zero : ∀ {i} → BinaryTreePath i
zero = branchL λ where .force → zero

infinity : ∀ {i} → BinaryTreePath i
infinity = branchR λ where .force → infinity

Теперь я хочу определить значение, которое имеет более длинные повторяющиеся части, например, LRRL. Лучшее, что я могу написать прямо сейчас, - это следующее (что быстро становится утомительным).

sqrt2 : ∀ {i} → BinaryTreePath i
sqrt2 =
  branchL λ where .force → branchR λ where .force → branchR λ where .force → branchL λ where .force → sqrt2

-- or --

sqrt2 : ∀ {i} → BinaryTreePath i
sqrt2 = branchL λ where
  .force → branchR λ where
    .force → branchR λ where
      .force → branchL λ where
        .force → sqrt2

Цель

определять branchL' а также branchR' так что следующий проходит проверку типа и проверку завершения.

sqrt2 : ∀ {i} → BinaryTreePath i
sqrt2 = branchL' (branchR' (branchR' (branchL' sqrt2)))

Вещи, которые я пробовал до сих пор

Перенос части в обычную функцию не работает:

branchL' : (∀ {i} → BinaryTreePath i) → (∀ {j} → BinaryTreePath j)
branchL' path = branchL λ where .force → path

zero' : ∀ {i} → BinaryTreePath i
zero' = branchL' zero'
--               ^^^^^ Termination checking failed

Поэтому я попытался обернуть в макрос, но я не могу найти, как построить термин branchL λ where .force → path когда path дается как Term, Следующее также не работает:

open import Agda.Builtin.Reflection
open import Data.Unit
open import Data.List

macro
  branchL' : Term → Term → TC ⊤
  branchL' v hole = do
    path ← unquoteTC v
    term ← quoteTC (branchL λ where .force → path)
    --                                       ^^^^ error
    unify hole term

{- error message:
Cannot instantiate the metavariable _32 to solution BinaryTreePath
.j since it contains the variable .j which is not in scope of the
metavariable or irrelevant in the metavariable but relevant in the
solution
when checking that the expression path' has type BinaryTreePath .j
-}

1 ответ

Решение

Вместо того чтобы писать branchL' а также branchR' Могу ли я предложить имитировать то, что мы делаем в Codata.Stream определить разворачивание цикла?

Основная идея заключается в том, что мы можем определить вспомогательные функции, которые используют Thunk в их типе и, таким образом, гарантируют, что они используют свои аргументы в осторожной манере.

Первый шаг - определить небольшой язык Choice с можно сделать и дать ему семантику с точки зрения BinaryTreePath:

data Choice : Set where
  L R : Choice

choice : ∀ {i} → Choice → Thunk BinaryTreePath i → BinaryTreePath i
choice L t = branchL t
choice R t = branchR t

Затем мы можем отменить эту семантику, чтобы она работала не только для отдельных вариантов, но и для списков вариантов:

open import Data.List

_<|_ : ∀ {i} → List Choice → BinaryTreePath i → BinaryTreePath i
[]       <| t = t
(c ∷ cs) <| t = choice c (λ where .force → cs <| t)

Теперь наступает решающий момент: если у нас есть непустой список вариантов, мы статически знаем, что путь, по которому он приведет, будет защищен.

open import Data.List.NonEmpty

_⁺<|_ : ∀ {i} → List⁺ Choice → Thunk BinaryTreePath i → BinaryTreePath i
(c ∷ cs) ⁺<| t = choice c (λ where .force → cs <| t .force)

Используя этот комбинатор, мы можем легко определить cycle:

cycle : ∀ {i} → List⁺ Choice → BinaryTreePath i
cycle cs = cs ⁺<| (λ where .force → cycle cs)

И тогда ваш пример получен напрямую с помощью цикла:

sqrt2 : ∀ {i} → BinaryTreePath i
sqrt2 = cycle (L ∷ R ∷ R ∷ L ∷ [])

Я поместил код в отдельную суть.

Другие вопросы по тегам