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]
MonadTrans
instanciaT
paraM
y unaMonad
instancia paraN
, entoncesT.liftM
se puede usar para elevar un valor de tipoN[A]
a un valor de tipoM[N, A]
.liftM
para eso, pero no pude entender cómo hacerlo correctamente. Chicos, eres rock!f
ser una instancia, nores0
?Otro uso del levantamiento que he encontrado en los documentos (no necesariamente relacionados con Scala) es sobrecargar una función
f: A -> B
conf: List[A] -> List[B]
(o conjuntos, conjuntos múltiples, ...). Esto se usa a menudo para simplificar las formalizaciones porque no importa sif
se aplica a un elemento individual o a varios elementos.Este tipo de sobrecarga a menudo se realiza de forma declarativa, por ejemplo,
o
o imperativamente, por ejemplo,
fuente
Tenga en cuenta que cualquier colección que se extienda
PartialFunction[Int, A]
(como lo señala oxbow_lakes) puede levantarse; así por ejemploque convierte una función parcial en una función total donde se asignan valores no definidos en la colección
None
,Además,
Esto muestra un enfoque ordenado para evitar excepciones de índice fuera de límites .
fuente
También hay unlifting , que es el proceso inverso al levantamiento.
Si el levantamiento se define como
entonces no levantar es
La biblioteca estándar de Scala define
Function.unlift
comoPor ejemplo, la biblioteca play-json proporciona unlift para ayudar con la construcción de los serializadores JSON :
fuente