Exit e-book
Show all chapters
03
Una función pura debe ser determinística y debe depender sólo de sus entradas
03. 
Una función pura debe ser determinística y debe depender sólo de sus entradas
Introducción a la Programación con Efectos Funcionales usando ZIO
03

Una función pura debe ser determinística y debe depender sólo de sus entradas

La segunda característica de una función es que debe ser determinística y debe depender sólo de sus entradas. Esto significa que para cada entrada que se provea a la función siempre se debe obtener la misma salida, sin importar cuántas veces la función sea llamada. Por ejemplo, la siguiente función para generar enteros aleatorios no es determinística:


def generateRandomInt(): Int = (new scala.util.Random).nextInt

Para demostrar por qué esta función no es determinística,  consideremos qué pasa la primera vez que llamamos a la función:


generateRandomInt() // Resultado: -272770531

Y luego, qué pasa si llamamos otra vez a la función:


generateRandomInt() // Resultado: 217937820

¡Obtenemos resultados diferentes! Claramente esta función no es determinística, y su encabezado es engañoso otra vez, porque sugiere que no depende de ninguna entrada para producir una salida, cuando en realidad hay una dependencia escondida hacia un objeto scala.util.Random. Esto podría causar problemas, porque nunca podemos estar realmente seguros de cómo la función generateRandomInt se va a comportar, haciéndola difícil de testear.

Ahora, veamos una definición alternativa. Para ello, usaremos un generador de números aleatorios personalizado, basado en un ejemplo presentado en el libro Functional Programming in Scala:

 


final case class RNG(seed: Long) {
  def nextInt: (Int, RNG) = {
    val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL
    val nextRNG = RNG(newSeed)
    val n       = (newSeed >>> 16).toInt
    (n, nextRNG)
  }
}

def generateRandomInt(random: RNG): (Int, RNG) = random.nextInt


Esta nueva versión de la función generateRandomInt es determinística: no importa cuántas veces se la llame, siempre obtendremos la misma salida para las misma entrada y el encabezado ahora muestra claramente la dependencia hacia la variable random. Por ejemplo:


val random        = RNG(10)
val (n1, random1) = generateRandomInt(random) // n1 = 3847489, random1 = RNG(252149039181)
val (n2, random2) = generateRandomInt(random) // n2 = 3847489, random2 = RNG(252149039181)

Si queremos generar un nuevo entero, debemos proveer una entrada distinta:


val (n3, random3) = generateRandomInt(random2) // n3 = 1334288366, random3 = RNG(87443922374356)

PREVIOUS
Chapter
02
NEXT
Chapter
04