Зависимые типы в Haskell

    Котельников Евгений

        SpbHUG/FProg


       12 июля 2012
Типы и термы


  В большинстве языков программирования существует как
  минимум два мира — мир термов и мир типов.
  Мир типов
  Integer, String → String, ∀ Nat. Nat → String

  Мир термов
  10, "foobar", n, x -> x + 1, 6 + 5
  Между объектами этих двух миров могут возникать
  функциональные зависимости.
Типы и термы

  Зависимость терма от терма
  int increment(int x) {
    return x + 1 ;
  }


  Зависимость терма от типа
  Полиморфные функции
  static <T> List<T> singletonList(T elem) {
    ArrayList<T> list = new ArrayList<T>( ) ;
    list.add(elem) ;
    return list ;
  }
Типы и термы


  Зависимость типа от типа
  Операторы над типами
  Collection <A>


  Зависимость типа от терма
  Зависимые типы
  nil : {A : Type} → Vector 0 A
  cons : {n : Nat} → {A : Type} →
            Vector n A → Vector (n + 1) A
Выражаемое в зависимых типах


     Операции над списками и матрицами с
     контролируемыми размерностями
     append : {n m : Nat} → {A : Type} →
                 Vector n A → Vector m A → Vector (n + m) A

     append nil ys = ys
     append (cons x xs) ys = cons x (append xs ys)

     Отсортированные списки
     Сбалансированные деверья
     Функции с переменным числом аргументов
     Типо-безопасные printf и scanf
Код, содержащий доказательства

  Системы зависимых типов порождают предикатную логику
  (см. соответствие Карри-Говарда). Тип — утверждение о
  коде, терм — доказательство.
  Класс типа функтора в Coq
  Class Functor (𝜑 : Type → Type) := {
    fmap : ∀ {𝛼 𝛽} , (𝛼 → 𝛽) → 𝜑 𝛼 → 𝜑 𝛽 ;

       (** Functor laws **)
       fmap_id : ∀ 𝛼 (x : 𝜑 𝛼) , fmap id x = x ;
       fmap_comp : ∀ 𝛼 𝛽 𝛾 (x : 𝜑 𝛼) (f : 𝛽 → 𝛾) (g : 𝛼 → 𝛽) ,
                   fmap (f ∘ g) x = (fmap f ∘ fmap g) x
  }.
  Экземпляры класса, помимо самой реализации fmap,
  обязаны предоставить доказательства теорем о ней.
Экземпляр класса типа функтора
  Instance list_functor : Functor list := {
     fmap 𝛼 𝛽 := fix fmap f xs := match xs with
                                  | [] ⇒ []
                                  | x : : xs ⇒ (f x) : : (fmap f xs)
                                  end
  }.
  Proof .
  (** fmap_id **)
  intros .
  induction x as [| x xs] .
     reflexivity .
     rewrite IHxs . reflexivity .
  (** fmap_comp **)
  intros .
  induction x as [| x xs] .
     reflexivity .
     rewrite IHxs . reflexivity .
  Qed .
Вычисления в типах

  Пример: умножение матриц
  multiply : {n m q : Nat} →
                 Matrix m n → Matrix n q → Matrix m q


  Проблема
  Необходимо на этапе компиляции проверить, что ширина
  одной матрицы равна высоте другой. А в общем случае —
  проверить на равенство два терма.

  Решение (?)
  Вычислить оба терма до достижения нормальной формы и
  сравнивать их. Но вычисление может никогда не
  завершиться...
Вычислительные модели в типах

  Допускать только термы специального вида
  Вместо редукций — разрешение системы ограничений.
  Dependent ML, ATS

  Контроль над временем проверки типов
  Лимит на макс. допустимое количество шагов редукции.
  Cayenne

  Сильная нормализация
  Общая рекурсия запрещена, везде тотальные функции.
  Coq

  Отдельная проверка на тотальность
  Общая рекурсия разрешена, но отдельный механизм
  пытается распознать нетотальные функции.
  Agda, Epigram, Idris
Зависимые типы в Haskell
  Идея
  Определить термы, структура которых повторяет структуру
  своих типов.

  Натуральные числа в арифметике Пеано
  data Nat = Zero | Suc Nat


  Натуральные числа как singleton types
  data Zero = Zero
  data Suc n = Suc n


  Структура типа повторяет структуру терма
  Suc Zero : : Suc Zero
  Suc $ Suc $ Suc Zero : : Suc (Suc (Suc Zero) )
Определение типа вектора


  Обобщённые алгебраические типы данных
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE GADTSyntax #-}

  data Vector n a where
    VNil : : Vector Zero a
    VCons : : a → Vector n a → Vector (Suc n) a


  В типе вектора содержится длина
  vec4 : : Vector (Suc (Suc (Suc (Suc Zero) ) ) ) Int
  vec4 = VCons 1 $ VCons 2 $ VCons 3 $ VCons 4 VNil
Безопасные head и tail

  Ограничение на длину задано в типе
  vhead : : Vector (Suc n) a → a
  vhead (VCons a _) = a

  vtail : : Vector (Suc n) a → Vector n a
  vtail (VCons _ v) = v


  Проверка типов отвергает некорректные применения
  vhead (VCons 1 $ VCons 2 VNil) -- ok
  vhead VNil -- type error

  vtail (VCons 1 $ VCons 2 VNil) -- ok
  vtail VNil -- type error
K-тый элемент вектора

  Мультипараметрические классы типов
  {-# LANGUAGE MultiParamTypeClasses #-}

  class Kth n k where
    kth : : Vector n a → k → a

  instance Kth (Suc n’) Zero where
    kth (VCons a _) _ = a

  instance Kth n’ k’ ⇒ Kth (Suc n’) (Suc k’) where
    kth (VCons _ v) (Suc k’) = nth v k’


  Pattern matching по типам вместо термов
  kth (VCons 1 $ VCons 2 $ VCons 3 VNil) (Suc $ Suc Zero) -- ok
  kth (VCons 1 $ VCons 2 VNil) (Suc $ Suc $ Suc Zero) -- fail
Конкатенация векторов
  Семейства типов
  {-# LANGUAGE TypeFamilies #-}
  type family Plus n m
  type instance Plus Zero m = m
  type instance Plus (Suc n’) m = Suc (Plus n’ m)


  Можно инфиксно!
  {-# LANGUAGE TypeOperators #-}
  type family n :+ m
  type instance Zero :+ m = m
  type instance (Suc n’) :+ m = Suc (n’ :+ m)


  Конкатенация, сохраняющая длину
  append : : Vector n a → Vector m a → Vector (n :+ m) a
  append VNil v = v
  append (VCons a w) v = VCons a (append w v)
Литература: зависимые типы



   1. B. Pierce. Types and Programming Languages. Chapter
      30.5
   2. B. Pierce et al. Advanced Topics in Types and
      Programming Languages. Chapter 2
   3. T. Altenkirch, C. McBride, J. McKinna. Why Dependent
      Types Matter
   4. A. Bove, P. Dybjer. Dependent Types at Work
   5. U. Norell. Dependently Typed Programming in Agda
   6. H. Xi. Dependent Types in Practical Programming
Литература: программирование в типах Haskell




    1. C. McBride. Faking It: Simulation Dependent Types in
       Haskell
    2. O. Kiselyov, S. Peyton Jones, C. Shan. Fun with type
       functions
    3. B. Yorgey. Typed type-level programming in Haskell
    4. W. Jeltsch. Dependently typed programming and theorem
       proving in Haskell
Вопросы?

Евгений Котельников. Зависимые типы в Haskell

  • 1.
    Зависимые типы вHaskell Котельников Евгений SpbHUG/FProg 12 июля 2012
  • 2.
    Типы и термы В большинстве языков программирования существует как минимум два мира — мир термов и мир типов. Мир типов Integer, String → String, ∀ Nat. Nat → String Мир термов 10, "foobar", n, x -> x + 1, 6 + 5 Между объектами этих двух миров могут возникать функциональные зависимости.
  • 3.
    Типы и термы Зависимость терма от терма int increment(int x) { return x + 1 ; } Зависимость терма от типа Полиморфные функции static <T> List<T> singletonList(T elem) { ArrayList<T> list = new ArrayList<T>( ) ; list.add(elem) ; return list ; }
  • 4.
    Типы и термы Зависимость типа от типа Операторы над типами Collection <A> Зависимость типа от терма Зависимые типы nil : {A : Type} → Vector 0 A cons : {n : Nat} → {A : Type} → Vector n A → Vector (n + 1) A
  • 5.
    Выражаемое в зависимыхтипах Операции над списками и матрицами с контролируемыми размерностями append : {n m : Nat} → {A : Type} → Vector n A → Vector m A → Vector (n + m) A append nil ys = ys append (cons x xs) ys = cons x (append xs ys) Отсортированные списки Сбалансированные деверья Функции с переменным числом аргументов Типо-безопасные printf и scanf
  • 6.
    Код, содержащий доказательства Системы зависимых типов порождают предикатную логику (см. соответствие Карри-Говарда). Тип — утверждение о коде, терм — доказательство. Класс типа функтора в Coq Class Functor (𝜑 : Type → Type) := { fmap : ∀ {𝛼 𝛽} , (𝛼 → 𝛽) → 𝜑 𝛼 → 𝜑 𝛽 ; (** Functor laws **) fmap_id : ∀ 𝛼 (x : 𝜑 𝛼) , fmap id x = x ; fmap_comp : ∀ 𝛼 𝛽 𝛾 (x : 𝜑 𝛼) (f : 𝛽 → 𝛾) (g : 𝛼 → 𝛽) , fmap (f ∘ g) x = (fmap f ∘ fmap g) x }. Экземпляры класса, помимо самой реализации fmap, обязаны предоставить доказательства теорем о ней.
  • 7.
    Экземпляр класса типафунктора Instance list_functor : Functor list := { fmap 𝛼 𝛽 := fix fmap f xs := match xs with | [] ⇒ [] | x : : xs ⇒ (f x) : : (fmap f xs) end }. Proof . (** fmap_id **) intros . induction x as [| x xs] . reflexivity . rewrite IHxs . reflexivity . (** fmap_comp **) intros . induction x as [| x xs] . reflexivity . rewrite IHxs . reflexivity . Qed .
  • 8.
    Вычисления в типах Пример: умножение матриц multiply : {n m q : Nat} → Matrix m n → Matrix n q → Matrix m q Проблема Необходимо на этапе компиляции проверить, что ширина одной матрицы равна высоте другой. А в общем случае — проверить на равенство два терма. Решение (?) Вычислить оба терма до достижения нормальной формы и сравнивать их. Но вычисление может никогда не завершиться...
  • 9.
    Вычислительные модели втипах Допускать только термы специального вида Вместо редукций — разрешение системы ограничений. Dependent ML, ATS Контроль над временем проверки типов Лимит на макс. допустимое количество шагов редукции. Cayenne Сильная нормализация Общая рекурсия запрещена, везде тотальные функции. Coq Отдельная проверка на тотальность Общая рекурсия разрешена, но отдельный механизм пытается распознать нетотальные функции. Agda, Epigram, Idris
  • 10.
    Зависимые типы вHaskell Идея Определить термы, структура которых повторяет структуру своих типов. Натуральные числа в арифметике Пеано data Nat = Zero | Suc Nat Натуральные числа как singleton types data Zero = Zero data Suc n = Suc n Структура типа повторяет структуру терма Suc Zero : : Suc Zero Suc $ Suc $ Suc Zero : : Suc (Suc (Suc Zero) )
  • 11.
    Определение типа вектора Обобщённые алгебраические типы данных {-# LANGUAGE GADTs #-} {-# LANGUAGE GADTSyntax #-} data Vector n a where VNil : : Vector Zero a VCons : : a → Vector n a → Vector (Suc n) a В типе вектора содержится длина vec4 : : Vector (Suc (Suc (Suc (Suc Zero) ) ) ) Int vec4 = VCons 1 $ VCons 2 $ VCons 3 $ VCons 4 VNil
  • 12.
    Безопасные head иtail Ограничение на длину задано в типе vhead : : Vector (Suc n) a → a vhead (VCons a _) = a vtail : : Vector (Suc n) a → Vector n a vtail (VCons _ v) = v Проверка типов отвергает некорректные применения vhead (VCons 1 $ VCons 2 VNil) -- ok vhead VNil -- type error vtail (VCons 1 $ VCons 2 VNil) -- ok vtail VNil -- type error
  • 13.
    K-тый элемент вектора Мультипараметрические классы типов {-# LANGUAGE MultiParamTypeClasses #-} class Kth n k where kth : : Vector n a → k → a instance Kth (Suc n’) Zero where kth (VCons a _) _ = a instance Kth n’ k’ ⇒ Kth (Suc n’) (Suc k’) where kth (VCons _ v) (Suc k’) = nth v k’ Pattern matching по типам вместо термов kth (VCons 1 $ VCons 2 $ VCons 3 VNil) (Suc $ Suc Zero) -- ok kth (VCons 1 $ VCons 2 VNil) (Suc $ Suc $ Suc Zero) -- fail
  • 14.
    Конкатенация векторов Семейства типов {-# LANGUAGE TypeFamilies #-} type family Plus n m type instance Plus Zero m = m type instance Plus (Suc n’) m = Suc (Plus n’ m) Можно инфиксно! {-# LANGUAGE TypeOperators #-} type family n :+ m type instance Zero :+ m = m type instance (Suc n’) :+ m = Suc (n’ :+ m) Конкатенация, сохраняющая длину append : : Vector n a → Vector m a → Vector (n :+ m) a append VNil v = v append (VCons a w) v = VCons a (append w v)
  • 15.
    Литература: зависимые типы 1. B. Pierce. Types and Programming Languages. Chapter 30.5 2. B. Pierce et al. Advanced Topics in Types and Programming Languages. Chapter 2 3. T. Altenkirch, C. McBride, J. McKinna. Why Dependent Types Matter 4. A. Bove, P. Dybjer. Dependent Types at Work 5. U. Norell. Dependently Typed Programming in Agda 6. H. Xi. Dependent Types in Practical Programming
  • 16.
    Литература: программирование втипах Haskell 1. C. McBride. Faking It: Simulation Dependent Types in Haskell 2. O. Kiselyov, S. Peyton Jones, C. Shan. Fun with type functions 3. B. Yorgey. Typed type-level programming in Haskell 4. W. Jeltsch. Dependently typed programming and theorem proving in Haskell
  • 17.