Existem alguns usos:
PartialFunction
Lembre-se de a PartialFunction[A, B]é uma função definida para algum subconjunto do domínio A(conforme especificado pelo isDefinedAtmétodo). Você pode "elevar" a PartialFunction[A, B]para a Function[A, Option[B]]. Ou seja, uma função definida sobre o conjunto de Amas cujos valores são do tipoOption[B]
Isso é feito pela invocação explícita do método lifton 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
Você pode "elevar" uma chamada de método para uma função. Isso se chama eta-expansão (obrigado a Ben James por isso). Então, por exemplo:
scala> def times2(i: Int) = i * 2
times2: (i: Int)Int
Colocamos um método em uma função aplicando o sublinhado
scala> val f = times2 _
f: Int => Int = <function1>
scala> f(4)
res0: Int = 8
Observe a diferença fundamental entre métodos e funções. res0é uma instância (ou seja, é um valor ) do tipo (função)(Int => Int)
Functors
Um functor (como definido por scalaz ) é algum "contêiner" (eu uso o termo extremamente livremente), de Fmodo que, se tivermos F[A]uma função e A => B, então podemos colocar a mão em um F[B](pense, por exemplo, F = Liste no mapmétodo )
Podemos codificar esta propriedade da seguinte maneira:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
Isso é isomórfico por poder "elevar" a função A => Bpara o domínio do functor. Isso é:
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
Ou seja, se Fé um functor, e temos uma função A => B, temos uma função F[A] => F[B]. Você pode tentar implementar o liftmétodo - é bastante trivial.
Monad Transformers
Como o hcoopz diz abaixo (e eu acabei de perceber que isso me salvaria de escrever uma tonelada de código desnecessário), o termo "lift" também tem um significado dentro dos Monad Transformers . Lembre-se de que os transformadores de mônada são uma maneira de "empilhar" mônadas umas sobre as outras (as mônadas não compõem).
Por exemplo, suponha que você tenha uma função que retorne um IO[Stream[A]]. Isso pode ser convertido no transformador de mônada StreamT[IO, A]. Agora você pode querer "elevar" algum outro valor e IO[B]talvez também seja um StreamT. Você pode escrever isto:
StreamT.fromStream(iob map (b => Stream(b)))
Ou isto:
iob.liftM[StreamT]
isso levanta a questão: por que eu quero converter um IO[B]em um StreamT[IO, B]? . A resposta seria "tirar proveito das possibilidades de composição". Digamos que você tenha uma funçãof: (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]