Translate

jueves, 8 de junio de 2023

Primeros pasos con ZIO parte 4


ZIO puede convertir cualquier código (como una llamada a algún método) en un efecto, ya sea que ese código se llame síncrono (que devuelve directamente un valor) o asíncrono (que pasa un valor a las devoluciones de llamada).

Si se hace correctamente, cuando convierte el código en un efecto ZIO, este código se almacenará dentro del efecto para que ZIO pueda administrarlo y beneficiarse de funciones como reintentos, tiempos de espera y registro automático de errores.

Las funciones de conversión que tiene ZIO le permiten utilizar sin problemas todas las funciones de ZIO con código que no es de ZIO escrito en Scala o Java, incluidas las bibliotecas de terceros.

El código síncrono se puede convertir en un efecto ZIO usando ZIO.attempt:


import scala.io.StdIn

val readLine: ZIO[Any, Throwable, String] = ZIO.attempt(StdIn.readLine())


El tipo de error del efecto resultante siempre será Throwable, porque el código síncrono puede generar excepciones con cualquier valor de tipo Throwable.

Si sabe con certeza que algún código no arroja excepciones (excepto quizás excepciones en tiempo de ejecución), puede convertir el código en un efecto ZIO usando ZIO.succeed:


def printLine(line: String): UIO[Unit] =  ZIO.succeed(println(line))


A veces, es posible que sepa que el código arroja un tipo de excepción específico, y es posible que desee reflejar esto en el parámetro de error de su efecto ZIO.

Para ello, puede utilizar el método ZIO#refineToOrDie:


import java.io.IOException


val readLine2: ZIO[Any, IOException, String] =

  ZIO.attempt(StdIn.readLine()).refineToOrDie[IOException]


El código asíncrono que expone una API basada en devolución de llamada se puede convertir en un efecto ZIO usando ZIO.async:

object legacy {

  def login(

    onSuccess: User => Unit,

    onFailure: AuthError => Unit): Unit = ???

}


val login: ZIO[Any, AuthError, User] =

  ZIO.async[Any, AuthError, User] { callback =>

    legacy.login(

      user => callback(ZIO.succeed(user)),

      err  => callback(ZIO.fail(err))

    )

  }


Los efectos asincrónicos son mucho más fáciles de usar que las API basadas en devolución de llamadas y se benefician de las características de ZIO como la interrupción, la seguridad de los recursos y la gestión de errores.

Algunos códigos sincrónicos pueden participar en el llamado bloqueo de E/S, que pone un subproceso en un estado de espera, mientras espera que se complete alguna llamada del sistema operativo. Para obtener el máximo rendimiento, este código no debe ejecutarse en el grupo de subprocesos principal de su aplicación, sino en un grupo de subprocesos especial dedicado a operaciones de bloqueo.

ZIO tiene un grupo de subprocesos de bloqueo integrado en el tiempo de ejecución y le permite ejecutar efectos allí con ZIO.blocking:


import scala.io.{ Codec, Source }


def download(url: String) =

  ZIO.attempt {

    Source.fromURL(url)(Codec.UTF8).mkString

  }


def safeDownload(url: String) =

  ZIO.blocking(download(url))


Como alternativa, si desea convertir el código de bloqueo directamente en un efecto ZIO, puede usar el método ZIO.attemptBlocking:


val sleeping =

  ZIO.attemptBlocking(Thread.sleep(Long.MaxValue))


El efecto resultante se ejecutará en el grupo de subprocesos de bloqueo de ZIO.

Si tiene algún código síncrono que responderá a Thread.interrupt de Java (como Thread.sleep o código basado en bloqueo), entonces puede convertir este código en un efecto ZIO interrumpible usando el método ZIO.attemptBlockingInterrupt.

Algunos códigos síncronos solo se pueden cancelar invocando algún otro código, que es responsable de cancelar el cálculo en ejecución. Para convertir dicho código en un efecto ZIO, puede usar el método ZIO.attemptBlockingCanceble:


import java.net.ServerSocket

import zio.UIO


def accept(l: ServerSocket) =

  ZIO.attemptBlockingCancelable(l.accept())(ZIO.succeed(l.close()))