Hay algunos usos:
Función parcial
Recuerde que a PartialFunction[A, B]
es una función definida para algún subconjunto del dominio A
(según lo especificado por el isDefinedAt
método). Puedes "levantar" PartialFunction[A, B]
a a Function[A, Option[B]]
. Es decir, una función definida sobre el conjunto de A
pero cuyos valores son de tipoOption[B]
Esto se realiza mediante la invocación explícita del método lift
en PartialFunction
.
scala> val pf: PartialFunction[Int, Boolean] = { case i if i > 0 => i % 2 == 0}
pf: PartialFunction[Int,Boolean] = <function1>
scala> pf.lift
res1: Int => Option[Boolean] = <function1>
scala> res1(-1)
res2: Option[Boolean] = None
scala> res1(1)
res3: Option[Boolean] = Some(false)
Métodos
Puede "elevar" una invocación de método a una función. Esto se llama eta-expansión (gracias a Ben James por esto). Así por ejemplo:
scala> def times2(i: Int) = i * 2
times2: (i: Int)Int
Elevamos un método a una función aplicando el guión bajo
scala> val f = times2 _
f: Int => Int = <function1>
scala> f(4)
res0: Int = 8
Tenga en cuenta la diferencia fundamental entre métodos y funciones. res0
es una instancia (es decir, es un valor ) del tipo (función)(Int => Int)
Functores
Un functor (como lo define scalaz ) es un "contenedor" (yo uso el término de forma extremadamente flexible), de F
modo que, si tenemos una F[A]
y una función A => B
, podemos tener en nuestras manos un F[B]
(piense, por ejemplo, F = List
y el map
método )
Podemos codificar esta propiedad de la siguiente manera:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
Esto es isomorfo para poder "elevar" la función A => B
al dominio del functor. Es decir:
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
Es decir, si F
es un functor, y tenemos una función A => B
, tenemos una función F[A] => F[B]
. Puede intentar implementar el lift
método, es bastante trivial.
Transformadores de mónada
Como dice hcoopz a continuación (y me acabo de dar cuenta de que esto me habría salvado de escribir una tonelada de código innecesario), el término "levantar" también tiene un significado dentro de Monad Transformers . Recuerde que los transformadores de una mónada son una forma de "apilar" mónadas una encima de la otra (las mónadas no se componen).
Entonces, por ejemplo, suponga que tiene una función que devuelve un IO[Stream[A]]
. Esto se puede convertir al transformador de mónada StreamT[IO, A]
. Ahora es posible que desee "elevar" algún otro valor IO[B]
tal vez a que también sea un StreamT
. Puedes escribir esto:
StreamT.fromStream(iob map (b => Stream(b)))
O esto:
iob.liftM[StreamT]
esto plantea la pregunta: ¿por qué quiero convertir un IO[B]
en a StreamT[IO, B]
? . La respuesta sería "aprovechar las posibilidades de composición". Digamos que tienes una funciónf: (A, B) => C
lazy val f: (A, B) => C = ???
val cs =
for {
a <- as //as is a StreamT[IO, A]
b <- bs.liftM[StreamT] //bs was just an IO[B]
}
yield f(a, b)
cs.toStream //is a Stream[IO[C]], cs was a StreamT[IO, C]