F#
El tipo de "unidad"
Buscar..
¿De qué sirve una tupla 0?
Una tupla de 2 o una tupla de 3 representa un grupo de elementos relacionados. (Puntos en el espacio 2D, valores RGB de un color, etc.) Una tupla 1 no es muy útil ya que podría reemplazarse fácilmente con un solo int
.
Un 0-tuple parece aún más inútil ya que no contiene absolutamente nada . Sin embargo, tiene propiedades que lo hacen muy útil en lenguajes funcionales como F #. Por ejemplo, el tipo de tupla 0 tiene exactamente un valor, generalmente representado como ()
. Todas las tuplas 0 tienen este valor, por lo que es esencialmente un tipo singleton. En la mayoría de los lenguajes de programación funcionales, incluido F #, esto se denomina tipo de unit
.
Las funciones que devuelven void
en C # devolverán el tipo de unit
en F #:
let printResult = printfn "Hello"
Ejecute eso en el intérprete interactivo de F #, y verá:
val printResult : unit = ()
Esto significa que el valor printResult
es de tipo unit
y tiene el valor ()
(la tupla vacía, el único valor del tipo de unit
).
Las funciones también pueden tomar el tipo de unit
como un parámetro. En F #, las funciones pueden parecer que no están tomando parámetros. Pero, de hecho, están tomando un solo parámetro de tipo unit
. Esta función:
let doMath() = 2 + 4
es en realidad equivalente a:
let doMath () = 2 + 4
Es decir, una función que toma un parámetro de tipo unit
y devuelve el valor int
6. Si observa la firma de tipo que el intérprete interactivo F # imprime cuando define esta función, verá:
val doMath : unit -> int
El hecho de que todas las funciones tomarán al menos un parámetro y devolverá un valor, incluso si ese valor es a veces un valor "inútil" como ()
, significa que la composición de la función es mucho más fácil en F # que en idiomas que no tienen el tipo de unit
. Pero ese es un tema más avanzado que veremos más adelante. Por ahora, solo recuerde que cuando ve la unit
en la firma de una función, o ()
en los parámetros de una función, ese es el tipo de tupla 0 que sirve para decir "Esta función toma, o devuelve, valores no significativos".
Aplazar la ejecución del código
Podemos usar el tipo de unit
como un argumento de función para definir funciones que no queremos que se ejecuten hasta más adelante. A menudo, esto es útil en tareas en segundo plano asíncronas, cuando el subproceso principal puede querer activar alguna funcionalidad predefinida del subproceso en segundo plano, como moverlo a un nuevo archivo, o si no se debe ejecutar un enlace let de inmediato:
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)
En el siguiente código, definimos el código para iniciar un "trabajador" que simplemente imprime el valor en el que está trabajando cada 2 segundos. Luego, el trabajador devuelve dos funciones que pueden usarse para controlarlo: una que lo mueve al siguiente valor para trabajar, y otra que lo detiene de funcionar. Estas deben ser funciones, ya que no queremos que sus cuerpos se ejecuten hasta que decidamos hacerlo, de lo contrario, el trabajador pasaría inmediatamente al segundo valor y cerraría sin haber hecho nada.
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
Entonces podemos empezar un trabajador haciendo
let nextValue, stopOnNextTick = startWorker 12
y el trabajo comenzará - si estamos en F # interactivo, veremos los mensajes impresos en la consola cada dos segundos. Entonces podemos correr
nextValue ()
y veremos los mensajes que indican que el valor en el que se está trabajando se ha movido al siguiente.
Cuando es hora de terminar de trabajar, podemos ejecutar el
stopOnNextTick ()
Función, que imprimirá el mensaje de cierre, luego saldrá.
El tipo de unit
es importante aquí para indicar "sin entrada": las funciones ya tienen toda la información que necesitan para trabajar, y la persona que llama no tiene permitido cambiar eso.