Ci sono alcuni usi:
funzione parziale
Ricorda che a PartialFunction[A, B]è una funzione definita per alcuni sottogruppi del dominio A(come specificato dal isDefinedAtmetodo). Puoi "sollevare" a PartialFunction[A, B]in a Function[A, Option[B]]. Cioè, una funzione definita sul intero di Ama i cui valori sono di tipoOption[B]
Questo viene fatto dall'invocazione esplicita del metodo liftsu 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)
metodi
È possibile "sollevare" l'invocazione di un metodo in una funzione. Questo si chiama eta-espansione (grazie a Ben James per questo). Quindi per esempio:
scala> def times2(i: Int) = i * 2
times2: (i: Int)Int
Solleviamo un metodo in una funzione applicando il trattino basso
scala> val f = times2 _
f: Int => Int = <function1>
scala> f(4)
res0: Int = 8
Nota la differenza fondamentale tra metodi e funzioni. res0è un'istanza (ovvero è un valore ) del tipo (funzione)(Int => Int)
funtori
Un functor (come definito da scalaz ) è un "contenitore" (uso il termine in modo molto approssimativo), in modo Ftale che, se abbiamo una F[A]e una funzione A => B, possiamo mettere le mani su un F[B](pensa, per esempio, F = Liste il mapmetodo )
Possiamo codificare questa proprietà come segue:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
Questo è isomorfo per essere in grado di "sollevare" la funzione A => Bnel dominio del funzione. Questo è:
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
Cioè, se Fè un funzione e abbiamo una funzione A => B, abbiamo una funzione F[A] => F[B]. Potresti provare ad implementare il liftmetodo - è piuttosto banale.
Trasformatori di monade
Come dice hcoopz di seguito (e mi sono appena reso conto che questo mi avrebbe salvato dalla scrittura di un sacco di codice non necessario), il termine "ascensore" ha anche un significato all'interno di Monad Transformers . Ricordiamo che i trasformatori di una monade sono un modo per "impilare" le monadi l'una sopra l'altra (le monadi non si compongono).
Quindi, ad esempio, supponiamo di avere una funzione che restituisce un IO[Stream[A]]. Questo può essere convertito nel trasformatore di monade StreamT[IO, A]. Ora potresti voler "sollevare" qualche altro valore e IO[B]forse anche quello StreamT. Puoi scrivere questo:
StreamT.fromStream(iob map (b => Stream(b)))
O questo:
iob.liftM[StreamT]
questo fa sorgere la domanda: perché voglio convertire un IO[B]in un StreamT[IO, B]? . La risposta sarebbe "sfruttare le possibilità di composizione". Diciamo che hai una funzionef: (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]