it-roy-ru.com

Использование Bound с переменными нескольких типов для абстракций

Я пробовал bound package - один игрушечный пример, с которым вы можете попробовать это System F. В отличие от примеров в документации пакета, в которых есть один параметр типа для переменной, связанной лямбда-выражением, System F будет иметь два параметра типа, один для обычных переменных (связанных обычной лямбда-абстракцией) и один для переменных типа (связанных абстракциями типов).

Я не совсем понимаю, как использовать пакет, но, глядя на примеры, у меня сложилось впечатление, что я должен начать с написания экземпляра Monad для типа выражения. Тем не менее, я столкнулся с проблемой, так как не могу придумать что-то, что проверяет тип, а также является «явно правильным» (то есть, кажется, интуитивно правильным при проверке). Пока у меня есть

{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE LambdaCase #-}

module SystemF where

import Bound
import Control.Monad
import Data.Bifunctor

-- e ::= x | λx : τ. e | e1 e2 | ΛX. e | e [τ]
-- t denotes type variables, e denotes expression variables
data Exp t e
  = Var e
  | Lam (Scope () (Exp t) e)
  | App (Exp t e) (Exp t e)
  | TyLam (Scope () (FlipExp e) t) -- Is this correct?
  | TyApp (Exp t e) (Type t)

newtype FlipExp e t = FlipExp { getExp :: Exp t e }

instance Functor (Exp t) where
  fmap = second

instance Bifunctor Exp where
  bimap f g = \case
    Var e -> Var (g e)
    Lam s -> Lam (bimapInScope f g s)
    App e1 e2 -> App (bimap f g e1) (bimap f g e2)
    TyLam s' -> TyLam (bimapInScope g f s')
    TyApp e t -> TyApp (bimap f g e) (fmap f t)
    where
      bimapInScope f g = Scope . bimap f (second (bimap f g)) . unscope

instance Applicative (Exp t) where
  pure = Var
  (<*>) = ap

instance Monad (Exp t) where
  x >>= f = case x of
    Var v -> f v
    Lam s -> Lam (s >>>= f)
    App e1 e2 -> App (e1 >>= f) (e2 >>= f)
    TyLam s ->
      -- tmp :: Exp (Var () (Exp t a) a
      -- but we need Exp (Var () (Exp t b)) b
      -- just applying >>= inside the first argument 
      -- is insufficient as the outer 'a' won't change
      let tmp = first (second getExp) $ getExp (unscope s)
      in error "How do I implement this?"
    TyApp e t -> TyApp (e >>= f) t

instance Functor (FlipExp e) where
  fmap = second

instance Bifunctor FlipExp where
  bimap f g = FlipExp . bimap g f . getExp

-- τ ::= X | τ -> τ | ∀ X . τ
data Type t
  = TVar t
  | TFun (Type t) (Type t)
  | TForall (Scope () Type t)
  deriving (Functor, Foldable, Traversable)

instance Applicative Type where
  pure = TVar
  (<*>) = ap

instance Monad Type where
  x >>= f = case x of
    TVar v -> f v
    TFun t1 t2 -> TFun (t1 >>= f) (t2 >>= f)
    TForall s -> TForall (s >>>= f)
  1. Возможно ли иметь экземпляр монады для Exp t? Если да, то как?
  2. Какова интуиция позади экземпляра Монады? Что касается монад State/Maybe, я считаю полезным думать о них как о цепочечных вычислениях (с точки зрения связывания), тогда как для таких структур, как списки, я считаю полезным думать с точки зрения выравнивания (с точки зрения объединения). ). Однако я не могу придумать какую-либо правильную интуицию для экземпляра Monad для Exp. Связывает ли точно замену, избегающую захвата? Я прочитал этот пост но затерялся после обычного раздела "Индексы де Брюйна".
6
theindigamer

Смотрите обсуждение здесь и пакет @ phadej's bound-extras здесь .

Суть в том, что абстракция типа - это вещь уровня термина (так что вариант Expr), которая должна абстрагироваться от Types. Обычный Scope b f a не подходит для этого, так как его расширение f (Either b (f a)) имеет фиксированную f для обоих случаев. Вы хотите, чтобы внешний f был Expr, а внутренний должен быть Type. Это приводит к следующим обобщениям Scope:

newtype ScopeH b f g a = ScopeH (g (Either b (f a)))
newtype ScopeT b t f a = ScopeT (t f (Either b (f a)))

newtype Expr' a b = Expr' (Expr b a)
data Expr b a
  = V a
  ...
  | TyApp (Expr b a) (Ty b)
  | Forall (ScopeH () (Expr' a) Ty b)
  ...

Expr' a исправляет индекс де Брюйна для термов vars, так что конструктор ScopeH может вводить дополнительный тип var для помещения в дыры b.

1
Sebastian Graf