Типи першого класу дозволяють щось називати залежним введенням тексту . Вони дозволяють програмісту використовувати значення типів на рівні типу. Наприклад, тип усіх пар цілих чисел є регулярним типом, тоді як пара всіх цілих чисел з лівим числом меншим, ніж праве число, є залежним типом. Стандартним вступним прикладом цього є кодовані по довжині списки (як правило, вони називаються Vector
в Haskell / Idris). Наступний псевдокод - це суміш Ідріса та Хаскелла.
-- a natural number
data Nat = Zero | Successor Nat
data Vector length typ where
Empty : Vector Zero typ
(::) : typ -> Vector length typ -> Vector (Successor length) typ
Цей фрагмент коду говорить нам про дві речі:
- Порожній список має нульову довжину.
cons
Якщо елемент перейти до списку, створюється список довжини n + 1
Це дуже схоже на інше поняття з 0 n + 1
, чи не так? Я повернусь до цього.
Що ми отримуємо від цього? Тепер ми можемо визначити додаткові властивості функцій, які ми використовуємо. Наприклад: Важливою властивістю append
є те, що довжина результуючого списку є сумою довжин двох списків аргументів:
plus : Nat -> Nat -> Nat
plus Zero n = n
plus (Successor m) n = Successor (plus m n)
append : Vector n a -> Vector m a -> Vector (plus n m) a
append Empty ys = ys
append (x::xs) ys = x :: append xs ys
Але в цілому ця техніка не здається всім корисною в повсякденному програмуванні. Як це стосується розеток, POST
/ GET
запитів тощо?
Ну не виходить (принаймні, не без значних зусиль). Але це може допомогти нам іншими способами:
Залежні типи дозволяють сформулювати інваріанти в кодових правилах, як функція повинна вести себе. Використовуючи ці дані, ми отримуємо додаткову безпеку щодо поведінки коду, подібно до попередніх та післяумов Ейфелева. Це надзвичайно корисно для автоматизованого доведення теореми, що є одним із можливих застосувань Ідріса.
Повертаючись до наведеного вище прикладу, визначення списків, кодованих довжиною, нагадує математичне поняття індукції . В Idris ви можете фактично сформулювати поняття індукції в такому списку таким чином:
-- If you can supply the following:
list_induction : (Property : Vector len typ -> Type) -> -- a property to show
(Property Empty) -> -- the base case
((w : a) -> (v : Vector n a) ->
Property v -> Property (w :: v)) -> -- the inductive step
(u : Vector m b) -> -- an arbitrary vector
Property u -- the property holds for all vectors
Ця методика обмежується конструктивними доказами, але, тим не менш, є дуже потужною. Можна спробувати написати append
індуктивно як вправу.
Звичайно, залежні типи - це лише одне використання типів першого класу, але це, мабуть, одне з найпоширеніших. Додаткове використання включає, наприклад, повернення певного типу з функції на основі його аргументів.
type_func : Vector n a -> Type
type_func Empty = Nat
type_func v = Vector (Successor Zero) Nat
f : (v : Vector n a) -> type_func v
f Empty = 0
f vs = length vs :: Empty
Це безглуздий приклад, але він демонструє те, що ви не можете наслідувати без типів першого класу.