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]
MonadTrans
instancjęT
dlaM
iMonad
instancję dlaN
, toT.liftM
można go użyć do podniesienia wartości typuN[A]
do wartości typuM[N, A]
.liftM
, ale nie udało mi się zrozumieć, jak to zrobić poprawnie. Chłopaki, jesteście rockowi!f
być instancja, prawdares0
?Innym zastosowaniem podnoszenia , na które natknąłem się w papierach (niekoniecznie związanych ze Scalą), jest przeciążanie funkcji za
f: A -> B
pomocąf: List[A] -> List[B]
(lub zestawów, multisetów, ...). Jest to często używane w celu uproszczenia formalizacji, ponieważ wtedy nie ma znaczenia, czyf
zostanie zastosowane do pojedynczego elementu czy do wielu elementów.Tego rodzaju przeciążenie często dokonuje się deklaratywnie, np.
lub
lub bezwzględnie, np.
źródło
Zauważ, że każda kolekcja, która się rozszerza
PartialFunction[Int, A]
(jak wskazano w oxbow_lakes) może zostać zniesiona; tak na przykładco zamienia funkcję częściową w funkcję całkowitą, na której odwzorowywane są wartości niezdefiniowane w kolekcji
None
,Ponadto,
To pokazuje zgrabne podejście do unikania indeksu poza wyjątkami.
źródło
Istnieje również unlifting , który jest odwrotnym procesem do podnoszenia.
Jeśli podnoszenie jest zdefiniowane jako
wtedy unlifting jest
Standardowa biblioteka Scala definiuje
Function.unlift
jakoNa przykład biblioteka play-json zapewnia unlift, aby pomóc w budowie serializatorów JSON :
źródło