Ricerca…
A che serve una tupla 0?
Una tupla di 2 o una tupla di 3 rappresentano un gruppo di elementi correlati. (Punti nello spazio 2D, valori RGB di un colore, ecc.) Una tupla 1 non è molto utile in quanto potrebbe essere facilmente sostituita con un singolo int
.
Una tupla 0 sembra ancora più inutile poiché non contiene assolutamente nulla . Tuttavia ha proprietà che lo rendono molto utile in linguaggi funzionali come F #. Ad esempio, il tipo di tupla 0 ha esattamente un valore, solitamente rappresentato come ()
. Tutte le tuple 0 hanno questo valore quindi è essenzialmente un tipo singleton. Nella maggior parte dei linguaggi di programmazione funzionale, incluso F #, questo è chiamato il tipo di unit
.
Le funzioni che restituiscono void
in C # restituiranno il tipo di unit
in F #:
let printResult = printfn "Hello"
Eseguilo nell'interprete interattivo F # e vedrai:
val printResult : unit = ()
Ciò significa che il valore printResult
è di tipo unit
e ha il valore ()
(la tupla vuota, l'unico valore del tipo di unit
).
Le funzioni possono anche prendere il tipo di unit
come parametro. In F #, le funzioni potrebbero sembrare che non stanno prendendo alcun parametro. Ma in effetti stanno prendendo un singolo parametro unit
di tipo. Questa funzione:
let doMath() = 2 + 4
è in realtà equivalente a:
let doMath () = 2 + 4
Cioè, una funzione che accetta un parametro di tipo unit
e restituisce il valore int
6. Se si osserva la firma del tipo che l'interprete interattivo F # stampa quando si definisce questa funzione, vedrete:
val doMath : unit -> int
Il fatto che tutte le funzioni richiedono almeno un parametro e restituiscono un valore, anche se tale valore è talvolta un valore "inutile" come ()
, significa che la composizione della funzione è molto più semplice in F # rispetto alle lingue che non hanno il tipo di unit
. Ma questo è un argomento più avanzato che vedremo più avanti. Per ora, ricorda che quando vedi l' unit
in una firma di funzione, o ()
nei parametri di una funzione, questo è il tipo di tupla 0 che funge da modo per dire "Questa funzione accetta o restituisce valori privi di significato".
Differire nell'esecuzione del codice
Possiamo usare il tipo di unit
come argomento di funzione per definire funzioni che non vogliamo vengano eseguite fino a più tardi. Questo è spesso utile in attività di background asincrone, quando il thread principale potrebbe voler attivare alcune funzionalità predefinite del thread in background, come magari spostarlo in un nuovo file, o se aa let-binding non dovrebbe essere eseguito immediatamente:
module Time =
let now = System.DateTime.Now // value is set and fixed for duration of program
let now() = System.DateTime.Now // value is calculated when function is called (each time)
Nel codice seguente, definiamo il codice per avviare un "worker" che stampa semplicemente il valore su cui sta lavorando ogni 2 secondi. L'operatore restituisce quindi due funzioni che possono essere utilizzate per controllarlo, una che la sposta al valore successivo su cui lavorare e una che ne impedisce il funzionamento. Queste devono essere funzioni, perché non vogliamo che i loro corpi siano eseguiti fino a quando non lo decidiamo, altrimenti il lavoratore si sposterebbe immediatamente al secondo valore e si spegnerebbe senza aver fatto nulla.
let startWorker value =
let current = ref value
let stop = ref false
let nextValue () = current := !current + 1
let stopOnNextTick () = stop := true
let rec loop () = async {
if !stop then
printfn "Stopping work."
return ()
else
printfn "Working on %d." !current
do! Async.Sleep 2000
return! loop () }
Async.Start (loop ())
nextValue, stopOnNextTick
Possiamo quindi iniziare un lavoratore facendo
let nextValue, stopOnNextTick = startWorker 12
e il lavoro inizierà - se siamo in F # interattiva, vedremo i messaggi stampati nella console ogni due secondi. Possiamo quindi correre
nextValue ()
e vedremo i messaggi che indicano che il valore su cui si sta lavorando è passato a quello successivo.
Quando è il momento di terminare il lavoro, possiamo eseguire il
stopOnNextTick ()
funzione, che stamperà il messaggio di chiusura, quindi uscirà.
Il tipo di unit
è importante qui per indicare "nessun input": le funzioni dispongono già di tutte le informazioni necessarie per lavorare su di esse e il chiamante non può modificarlo.