Sono consapevole del fatto che il concetto di invarianti esiste attraverso più paradigmi di programmazione. Ad esempio, invarianti di loop sono rilevanti in OO, programmazione funzionale e procedurale.
Tuttavia, un tipo molto utile trovato in OOP è un invariante dei dati di un particolare tipo. Questo è ciò che sto definendo "invarianti basati sui tipi" nel titolo. Ad esempio, un tipo Fraction
potrebbe avere un numerator
e denominator
, con l'invariante che il loro gcd è sempre 1 (cioè la frazione è in forma ridotta). Posso solo garantirlo avendo una specie di incapsulamento del tipo, non lasciando che i suoi dati siano impostati liberamente. In cambio, non devo mai verificare se è ridotto, quindi posso semplificare algoritmi come i controlli di uguaglianza.
D'altra parte, se dichiaro semplicemente un tipo Fraction
senza fornire questa garanzia tramite l'incapsulamento, non posso scrivere in modo sicuro alcuna funzione di questo tipo che presuppone che la frazione sia ridotta, perché in futuro qualcun altro potrebbe vieni e aggiungi un modo per ottenere una frazione non ridotta.
Generalmente, la mancanza di questo tipo di invariante potrebbe portare a:
- Gli algoritmi più complessi come pre-condizioni devono essere controllati / assicurati in più posizioni
- Le violazioni DRY come queste pre-condizioni ripetute rappresentano la stessa conoscenza di base (che l'invariante dovrebbe essere vero)
- Dover applicare le pre-condizioni tramite errori di runtime piuttosto che garanzie in fase di compilazione
Quindi la mia domanda è quale sia la risposta alla programmazione funzionale a questo tipo di invariante. Esiste un modo funzionale-idiomatico per ottenere più o meno la stessa cosa? O c'è qualche aspetto della programmazione funzionale che rende i benefici meno rilevanti?