Ci sono alcuni usi:
funzione parziale
Ricorda che a PartialFunction[A, B]
è una funzione definita per alcuni sottogruppi del dominio A
(come specificato dal isDefinedAt
metodo). Puoi "sollevare" a PartialFunction[A, B]
in a Function[A, Option[B]]
. Cioè, una funzione definita sul intero di A
ma i cui valori sono di tipoOption[B]
Questo viene fatto dall'invocazione esplicita del metodo lift
su 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 F
tale che, se abbiamo una F[A]
e una funzione A => B
, possiamo mettere le mani su un F[B]
(pensa, per esempio, F = List
e il map
metodo )
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 => B
nel 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 lift
metodo - è 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]