I tipi di prima classe abilitano qualcosa chiamato digitazione dipendente . Ciò consente al programmatore di utilizzare valori di tipi a livello di tipo. Ad esempio, il tipo di tutte le coppie di numeri interi è un tipo normale, mentre la coppia di tutti i numeri interi con il numero sinistro più piccolo del numero destro è un tipo dipendente. L'esempio introduttivo standard di questo è elenchi codificati in lunghezza (generalmente chiamati Vector
in Haskell / Idris). Il seguente pseudo-codice è una miscela di Idris e Haskell.
-- 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
Questo pezzo di codice ci dice due cose:
- L'elenco vuoto ha lunghezza zero.
cons
l'inserimento di un elemento in un elenco crea un elenco di lunghezza n + 1
Sembra molto simile a un altro concetto con 0 e n + 1
non è vero? Tornerò su quello.
Cosa ci guadagna da questo? Ora possiamo determinare proprietà aggiuntive delle funzioni che utilizziamo. Ad esempio: una proprietà importante di append
è che la lunghezza dell'elenco risultante è la somma delle lunghezze dei due elenchi di argomenti:
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
Ma tutto sommato questa tecnica non sembra tutto utile nella programmazione quotidiana. In che modo ciò si riferisce a socket, POST
/ GET
richieste e così via?
Beh, non lo fa (almeno non senza un notevole sforzo). Ma può aiutarci in altri modi:
I tipi dipendenti ci consentono di formulare invarianti nel codice - regole come il comportamento di una funzione. Usando questi abbiamo ulteriore sicurezza sul comportamento del codice, simile alle pre e post-condizioni di Eiffel. Ciò è estremamente utile per la dimostrazione di teoremi automatizzati, che è uno dei possibili usi di Idris.
Tornando all'esempio sopra, la definizione di elenchi codificati in lunghezza ricorda il concetto matematico di induzione . In Idris, puoi effettivamente formulare il concetto di induzione su un elenco come il seguente:
-- 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
Questa tecnica è limitata a prove costruttive, ma è comunque molto potente. Puoi provare a scrivere append
induttivamente come esercizio.
Naturalmente, i tipi dipendenti sono solo un uso dei tipi di prima classe, ma è probabilmente uno dei più comuni. Ulteriori usi includono, ad esempio, la restituzione di un tipo specifico da una funzione basata sui suoi argomenti.
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
Questo è un esempio senza senso, ma dimostra qualcosa che non puoi emulare senza tipi di prima classe.