Levantamiento en Scala
- Transformar métodos en funciones
- Transforme funciones puras en funtores en Scala
- Transformar funciones parciales a funciones en Scala
- Transformadores de mónadas en Scala
- Conclusión
Este artículo hablará sobre el levantamiento en el lenguaje de programación Scala. El levantamiento tiene diferentes significados; depende del contexto en que lo usemos.
Veámoslos uno por uno.
Transformar métodos en funciones
A veces nos encontramos con situaciones en las que queremos transformar métodos en funciones. Este es un ejemplo de levantamiento.
Por ejemplo, digamos que tenemos los siguientes métodos:
def mul(x: Int) = x*5
def isEven(x: Int) = x%2==0
Ahora, podemos componer estos métodos de la siguiente manera:
isEven(mul(4))
Pero, ¿y si queremos que esta composición suceda de manera funcional? Luego tenemos que transformar estos métodos anteriores en funciones.
Eso se puede hacer levantándolos usando el símbolo _
en Scala.
val mulAns = mul _
val isEvenAns = isEven _
Ahora, podemos componerlos funcionalmente de la siguiente manera:
( mulAns andThen isEvenAns)(5)
Código de trabajo completo:
object MyClass {
def mul(x: Int) = x*5
def isEven(x: Int) = x%2==0
def main(args: Array[String])
{
println(isEven(mul(4)));
val mulAns = mul _
val isEvenAns = isEven _
println(( mulAns andThen isEvenAns)(5))
}
}
Producción :
true
false
Explicación:
La primera salida es true
ya que 4 se multiplica por 5, lo que da 20. Este 20 se pasa como parámetro a la función isEven
.
La segunda salida es false
, ya que primero se multiplica 5 por 5 y luego se comprueba su resultado en la función isEven
.
Transforme funciones puras en funtores en Scala
Funtor se refiere al mapeo entre categorías, algo así como una función de entidades u objetos. Por ejemplo, tenemos las categorías A
y B
y un funtor F
que mapea los objetos de A
a los objetos de B
.
trait Functor[F[_]] {
def mapping[A, B](X: F[A])(f: A => B): F[B]
def lifting[A, B](f: A => B): F[A] => F[B] =
X => map(X)(f)
}
En este contexto, levantar se refiere a tomar una función de A=>B
y transformarla en un funtor haciéndola una función de la forma F[A] => F[B]
. Cuando se trabaja con tipos de datos anidados, esto puede ser muy útil.
Transformar funciones parciales a funciones en Scala
En este contexto, el levantamiento se refiere a la extensión del dominio. Las funciones parciales son tipos de funciones que se pueden aplicar a los subdominios de valores.
Pero a veces, es posible que queramos ampliar su dominio, y el levantamiento nos ayuda a lograrlo.
Supongamos que tenemos una función parcial que da la raíz cuadrada de un número positivo.
val sqrt: PartialFunction[Double, Double] =
{
case temp if temp >= 0 => Math.sqrt(temp)
}
Ahora, podemos extender el dominio de nuestra función parcial utilizando el método lift
para que se vea más elegante:
def getSqrtRootPartialFunction(t: Double) =
{
sqrt.lift(t).map(ans => s"Square root of ${t} is ${ans}")
.getOrElse(s"Cannot calculate square root for t")
}
Código de trabajo completo:
object MyClass {
val sqrt: PartialFunction[Double, Double] = {
case temp if temp >= 0 => Math.sqrt(temp)
}
def getSqrtRootPartialFunction(t: Double) = {
sqrt.lift(t).map(ans => println(s"Square root of ${t} is ${ans}"))
.getOrElse(println(s"Cannot calculate square root for t"))
}
def main(args: Array[String])
{
getSqrtRootPartialFunction(35)
}
}
Producción :
Square root of 35.0 is 5.916079783099616
Explicación:
Entonces, lo que ha hecho el levantamiento de la función sqrt
aquí es que extendió el dominio a un doble completo de PartialFunction[Double, Double]
a una Function[Double, Option[Double]]
.
A veces, el levantamiento de funciones parciales se usa para evitar la excepción de índice fuera de límite
.
Seq("1", "2", "3").lift(1) // Some("2")
Seq("1", "2", "3").lift(7) // none returned
El dominio se ha ampliado de String
a Option[String]
. Entonces, cuando tratamos de acceder a un valor fuera de límite, el Seq
levantado devuelve None
en lugar de lanzar una excepción out of bound
.
Transformadores de mónadas en Scala
La combinación de mónadas se realiza mediante transformadores de mónadas. Digamos que tenemos dos Futures
:
val helloWorld: Future[Option[String]] = Future.successful(Some("hello world"))
val language: Future[String] = Future.successful("this is Scala language")
Ahora, como ambos Futures
tienen diferentes dominios, podemos usar transformadores de mónadas elevando language
a OptionT
. Por esto, podemos manejar el resultado de una manera más consistente:
def MonadTransformer() = {
val message: OptionT[Future, String] = for {
hello <- OptionT(helloWorld)
name <- OptionT.liftF(language)
} yield println(s"$hello $name")
val result: Future[Option[String]] = message.value
Await.result(result, 1 second)
}
Conclusión
En este artículo, hemos aprendido el concepto de levantamiento y lo que significa en diferentes contextos. Es muy útil escribir nuestro código de una manera más componible e idiomática, lo que nos permite centrarnos más en la lógica comercial del código en lugar de perder el tiempo construyendo diferentes partes de él.