I commenti di Jack e tp1 (che in realtà dovrebbero essere le risposte) spiegano già come questo sia implementato nei linguaggi funzionali.
La mia risposta aggiunge un punto sui linguaggi non funzionali, in particolare quello piuttosto popolare nel settore dello sviluppo: C #.
In linguaggi come C # o Java, è effettivamente una pratica corrente creare tipi quando è necessario limitare un po 'i valori. Se l'unica cosa di cui hai bisogno è un intero positivo, finirai con la classe PositiveInt
, ma in generale quei wrapper rifletteranno un po 'di più la logica di business ( ProductPrice
, RebatePercentage
, ecc.), Di nuovo con convalida all'interno (un prezzo del prodotto dovrebbe essere superiore a zero, una percentuale di sconto non accetta un valore superiore a 100, ecc.)
I vantaggi e gli svantaggi e il rischio di andare troppo lontano con questi tipi sono stati recentemente discussi qui .
Una volta che hai il tuo wrapper, puoi iniziare ad aggiungere la logica ad esso, e specialmente la validazione. Fondamentalmente, poiché il wrapper nasconde il valore che avvolge, la logica di validazione sarà localizzata nel costruttore e può essere così semplice come:
if (value > 100)
{
throw new ArgumentOutOfRangeException("value", "The allowed range is (0..100].");
}
Un'altra possibilità offerta da C # è quella di utilizzare contratti di codice , che offre diversi vantaggi:
-
Il controllo statico cattura l'uso improprio di quei wrapper prima di raggiungere l'errore durante il runtime,
-
Anche i contratti di codice vengono applicati durante il runtime, si è sicuri che il contratto non sarà utilizzato in modo improprio anche se il controllo statico è disabilitato (o gli avvisi sono stati ignorati dagli sviluppatori),
-
Con gli invarianti che controllano il valore avvolto, non c'è modo di assegnare un valore non valido (con la limitazione che i controlli siano eseguiti quando un metodo inizia o termina, e non ad ogni passo durante l'esecuzione del metodo) ,
-
L'integrazione di Visual Studio rende il codice autodocumentante fornendo suggerimenti sui contratti ai chiamanti.
È con successo preso in pratica? Bene, ci sono migliaia di metodi all'interno dello stesso .NET Framework che contengono contratti di codice. Per il codice aziendale, si tratta soprattutto di quanto sia critico il codice. Se le conseguenze di un fallimento sono costose, potrebbe essere molto interessante utilizzare i contratti di codice. D'altra parte, i contratti di codice hanno un costo notevole in termini di tempo dello sviluppatore (assicurando che tutti i contratti funzionino bene su progetti tutt'altro che piccoli richiedono molto di tempo) e potrebbero non essere una buona idea per i progetti che non hanno necessariamente bisogno di questo livello di affidabilità.
Questo risponde anche alla tua altra domanda:
I'm wondering: just how far is it practical to take this?
Questa è una domanda di rigore rispetto alla flessibilità.
-
Se hai bisogno di sviluppare molto velocemente e accetti il rischio di avere errori di runtime, scegli un linguaggio tipizzato debolmente. Il vantaggio di scrivere meno codice, ad esempio print(123)
supera il rischio di problemi che possono essere più o meno difficili da eseguire il debug, ad esempio 123 + "4"
(potrebbe risultare in 127
o "1234"
?)
In questo caso, i tipi non esisteranno o saranno gestiti implicitamente dalla lingua / framework. Mentre si può ancora eseguire la convalida dell'intervallo (ad esempio per disinfettare l'input dell'utente), sembrerebbe strano fare tale convalida al di fuori delle interfacce con il mondo esterno.
-
Se lavori su un software vitale, utilizzerai una prova formale che richiederà molto tempo, ma assicurati che non ci siano errori nel codice.
In questo caso, è probabile che i tipi abbiano una convalida rigorosa: intervallo e precisione per valori numerici, lunghezza e caratteri consentiti per le stringhe, ecc.
-
Nel frattempo, scegli l'approccio che corrisponde maggiormente alle tue esigenze. Per la maggior parte dei software, i contratti di codice sarebbero eccessivi. Ma per la maggior parte dei software, avere un controllo di base all'interno di un type wrapper (come Percentage
) può essere una buona idea. Senza andare agli estremi (come discusso nel link già fornito sopra) di creare classi per tutto , potrebbe essere un buon compromesso avere alcuni tipi generici come Range<T>
o LengthLimitedString
che non sono così difficili da implementare in linguaggi come C # o Java.