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 isDefinedAt
método). Você pode "elevar" a PartialFunction[A, B]
para a Function[A, Option[B]]
. Ou seja, uma função definida sobre o conjunto de A
mas cujos valores são do tipoOption[B]
Isso é feito pela invocação explícita do método lift
on 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 F
modo 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 = List
e no map
mé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 => B
para 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 lift
mé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]