Lo scopo dei tipi di opzioni è di consentire al compilatore di sapere esattamente dove un valore può essere vuoto in una lingua dove null semplicemente non esiste (tipico per qualsiasi linguaggio che supporta i tipi di dati algebrici). In questo modo, il compilatore può dare errori nel compilatore se si dimentica di verificare la presenza di vuoto. Ecco un esempio di Rust, un linguaggio imperativo con una sintassi simile a C:
let my_number : Option<int> = read_number();
return 2 * my_number; // compile error
Questo non verrà compilato perché my_number
non contiene un int
ma un Option<int>
(cioè un int
che può essere vuoto). Il compilatore non consente la moltiplicazione su Option<int>
.
Pertanto, è necessario abbinare lo schema my_number
per estrarne il valore:
let my_number : Option<int> = read_number();
match (my_number) {
Some(my_actual_number) =>
return Some(2 * my_actual_number); // ok
None ->
return None; // fallback if it's empty
Il modello che combina forza il programmatore per gestire ogni possibile scenario: in questo caso, il caso None
. In questo modo impedisce il tipico problema di "dimenticato di controllare per null".
Tuttavia, in una lingua in cui null è pervasivo, i tipi di opzioni non sono altrettanto utili poiché tutto può già essere nullo in ogni caso. Quindi il compilatore non può davvero aiutarti (dato che inferire il nullability richiederebbe la risoluzione del problema di interruzione).
Il modello di oggetto nullo non è la stessa cosa, ma è un concetto correlato.
Nelle lingue che dispongono del supporto di prima classe per i tipi di opzioni, è consentito applicare un'operazione all'oggetto interno all'interno del tipo di opzione, se esistente. Serve come scorciatoia per l'attività comune di "fai X all'oggetto, ma se è vuoto, dimenticalo". Questo processo viene in genere chiamato "mappatura" e questa operazione può essere definita per tutti i tipi di opzioni .
L'operazione mappa accetta un oggetto opzione ( Option<T>
), oltre a una funzione che può agire sull'oggetto interno ( T -> R
) e produce il risultato della funzione racchiusa in un oggetto opzione ( Option<R>
).
In effetti, l'esempio sopra può essere riscritto in modo molto più sintetico utilizzando una mappa combinata con una funzione anonima:
let my_number : Option<int> = read_number();
return my_number.map(
// use anonymous function to specify what to do
|my_actual_number| 2 * my_actual_number
);
La mappa può essere pensata come una ricetta che:
- Se l'oggetto opzione non è vuoto, applica la funzione fornita e restituisci il risultato in
Some(...)
.
- Se l'oggetto opzione è di fatto vuoto, non fare nulla e restituire solo
None
.
Nel modello di oggetto nullo, si utilizza una classe "null" che possiede gli stessi metodi della classe non nulla (potrebbero essere fratelli nella gerarchia di ereditarietà), ma disposti in modo tale che le operazioni sul "null" "La classe semplicemente non fa niente. Quindi, il modello di oggetto nullo è simile in quanto realizza lo stesso obiettivo, anche se in un modo diverso, più orientato agli oggetti / alla dattilografia.