Подтвердить что ты не робот

Каков правильный путь к описанию зависимой лямбда-абстракции типа typecheck с использованием "bound"?

Я реализую простой язык с надписью, похожий на один описанный Леннартом Августсоном, а также используя bound для управления привязками.

Когда typechecking зависимый лямбда-член, например λt:* . λx:t . x, мне нужно:

  • "Enter" внешнее лямбда-связующее, путем создания экземпляра t чему-то
  • Typecheck λx:t . x, дающий ∀x:t . t
  • Pi-abstract t, дающий ∀t:* . ∀x:t . t

Если lambda не зависела, мне удалось избежать создания экземпляра t с его типом на шаге 1, так как тип все, что мне нужно знать о переменной, в то время как typechecking на шаге 2. Но на шаге 3 мне не хватает информации, чтобы решить, какие переменные абстрагироваться.

Я мог бы ввести новое предложение имени и создать экземпляр t с помощью Bound.Name.Name, содержащего как тип, так и уникальное имя. Но я думал, что с bound мне не нужно создавать новые имена.

Есть ли альтернативное решение, которое мне не хватает?

4b9b3361

Ответ 1

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

Фактически использование индексов немного связано с тем, что из-за механизма уровня типа, который отражает размер текущей области (или, другими словами, текущую глубину в выражении) через вложенность Var - s. Это требует использования полиморфной рекурсии или GADT. Это также мешает нам хранить контекст в государственной монаде (потому что размер и, следовательно, тип контекста изменяется по мере того, как мы рекурсируем). Интересно, хотя если бы мы могли использовать индексированную государственную монаду; это был бы забавный эксперимент. Но я отвлекся.

Простейшим решением является представление контекста как функции:

type TC  a = Either String a -- our checker monad
type Cxt a = a -> TC (Type a) -- the context

Ввод a по существу является индексом de Bruijn, и мы просматриваем тип, применяя функцию к индексу. Мы можем определить пустой контекст следующим образом:

emptyCxt :: Cxt a
emptyCxt = const $ Left "variable not in scope"

И мы можем расширить контекст:

consCxt :: Type a -> Cxt a -> Cxt (Var () a)
consCxt ty cxt (B ()) = pure (F <$> ty)
consCxt ty cxt (F a)  = (F <$>) <$> cxt a

Размер контекста кодируется в Var вложенности. Увеличение размера видно здесь в обратном типе.

Теперь мы можем написать средство проверки типов. Главное, что мы используем fromScope и toScope, чтобы получить под связующими, и переносим по соответствующим расширенным Cxt (чей тип строк просто отлично).

data Term a
  = Var a
  | Star -- or alternatively, "Type", or "*"
  | Lam (Type a) (Scope () Term a)
  | Pi  (Type a) (Scope () Term a)
  | App (Type a) (Term a)  
  deriving (Show, Eq, Functor)

-- boilerplate omitted (Monad, Applicative, Eq1, Show1 instances)

-- reduce to normal form
rnf :: Term a -> Term a
rnf = ...

-- Note: IIRC "Simply easy" and Augustsson post reduces to whnf
-- when type checking. I use here plain normal form, because it 
-- simplifies the presentation a bit and it also works fine.

-- We rely on Bound alpha equality here, and also on the fact
-- that we keep types in normal form, so there no need for
-- additional reduction. 
check :: Eq a => Cxt a -> Type a -> Term a -> TC ()
check cxt want t = do
  have <- infer cxt t
  when (want /= have) $ Left "type mismatch"

infer :: Eq a => Cxt a -> Term a -> TC (Type a)
infer cxt = \case
  Var a -> cxt a
  Star  -> pure Star -- "Type : Type" system for simplicity
  Lam ty t -> do
    check cxt Star ty
    let ty' = rnf ty
    Pi ty' . toScope <$> infer (consCxt ty' cxt) (fromScope t)
  Pi ty t -> do
    check cxt Star ty
    check (consCxt (rnf ty) cxt) Star (fromScope t)
    pure Star
  App f x -> 
    infer cxt f >>= \case
      Pi ty t -> do
        check cxt ty x
        pure $ rnf (instantiate1 x t)
      _ -> Left "can't apply non-function"

Здесь рабочий код, содержащий приведенные выше определения. Надеюсь, я не испортил это слишком плохо.