Istnieje kilka zastosowań:
Częściowa funkcja
Pamiętaj, że a PartialFunction[A, B]
jest funkcją zdefiniowaną dla niektórych podzbiorów domeny A
(określonych przez isDefinedAt
metodę). Możesz „podnieść” a PartialFunction[A, B]
do Function[A, Option[B]]
. Oznacza to, że funkcja zdefiniowana nad cała of A
ale których wartości są typuOption[B]
Odbywa się to poprzez jawne wywołanie metody lift
na 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)
Metody
Możesz „podnieść” wywołanie metody do funkcji. Nazywa się to eta-ekspansją (dzięki za to Benowi Jamesowi). Na przykład:
scala> def times2(i: Int) = i * 2
times2: (i: Int)Int
Podnosimy metodę do funkcji, stosując znak podkreślenia
scala> val f = times2 _
f: Int => Int = <function1>
scala> f(4)
res0: Int = 8
Zwróć uwagę na zasadniczą różnicę między metodami i funkcjami. res0
jest instancją (tj. wartością ) typu (funkcji)(Int => Int)
Functors
Funktor (zgodnie z definicją scalaz ) jest jakiś „pojemnik” (używam tego terminu bardzo luźno), F
tak, że jeśli mamy F[A]
i funkcję A => B
, to możemy dostać w swoje ręce F[B]
(myślę, na przykład, F = List
a map
metoda )
Możemy zakodować tę właściwość w następujący sposób:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
Jest to izomorficzne w stosunku do możliwości „podniesienia” funkcji A => B
do dziedziny funktora. To jest:
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
To znaczy, jeśli F
jest funktorem, a my mamy funkcję A => B
, mamy funkcję F[A] => F[B]
. Możesz spróbować zaimplementować tę lift
metodę - jest to dość trywialne.
Transformatory Monad
Jak mówi poniżej hcoopz (i właśnie zdałem sobie sprawę, że uratowałoby mnie to od napisania mnóstwa niepotrzebnego kodu), termin „lift” ma również znaczenie w Monad Transformers . Przypomnij sobie, że transformatory monadowe są sposobem „układania” monad jeden na drugim (monady się nie komponują).
Załóżmy na przykład, że masz funkcję, która zwraca an IO[Stream[A]]
. Można to przekonwertować na transformator monadowy StreamT[IO, A]
. Teraz możesz chcieć „podnieść” jakąś inną wartość i IO[B]
być może dlatego, że jest to również StreamT
. Możesz albo napisać to:
StreamT.fromStream(iob map (b => Stream(b)))
Albo to:
iob.liftM[StreamT]
rodzi się pytanie: dlaczego chcę przekonwertować plik IO[B]
na StreamT[IO, B]
? . Odpowiedź brzmiałaby: „skorzystać z możliwości kompozycji”. Powiedzmy, że masz funkcjęf: (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]