Системы зависимых типов позволяют оперировать данными на уровне типов, что может значительно повысить точность спецификации программ. Несмотря на отсутствие поддержки самих зависимых типов в Haskell, некоторые их свойства могут быть реализованы с помощью расширений языка. Будет представлен ряд техник, приближающий Haskell к возможностям языков вроде Agda, Coq и Epigram. Доклад имеет вводный характер и не требует предварительных знаний в обсуждаемых в нём темах.
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