Co to jest "lifting" w Scali?
Czasami, gdy czytam artykuły w ekosystemie Scali, czytam termin "lifting" / "lifting". Niestety, nie jest wyjaśnione, co to dokładnie oznacza. Zrobiłem trochę badań i wydaje się, że podnoszenie ma coś wspólnego z wartościami funkcjonalnymi lub czymś w tym rodzaju, ale nie byłem w stanie znaleźć tekstu, który wyjaśnia, co tak naprawdę chodzi o podnoszenie w przyjazny dla początkujących sposób.
Istnieje dodatkowe zamieszanie poprzez framework Lift , który ma w nazwie Lift, ale to nie pomaga w odpowiedzi na pytanie.
Co to jest "lifting" w Scali?
4 answers
Istnieje kilka zastosowań:
Funkcja częściowa
Pamiętaj, że PartialFunction[A, B]
jest funkcją zdefiniowaną dla pewnego podzbioru domeny A
(zgodnie z metodą isDefinedAt
). Możesz" podnieść " PartialFunction[A, B]
do Function[A, Option[B]]
. Jest to funkcja zdefiniowana przez całość Z A
, ale której wartości są typu Option[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 funkcja. To się nazywa eta-ekspansja (Dzięki Benowi Jamesowi za to). Tak na przykład:
scala> def times2(i: Int) = i * 2
times2: (i: Int)Int
Podnosimy metodę do funkcji, stosując podkreślenie
scala> val f = times2 _
f: Int => Int = <function1>
scala> f(4)
res0: Int = 8
Zwróć uwagę na podstawową różnicę między metodami i funkcjami. res0
jest instancją (tzn. jest to wartość ) typu (function) (Int => Int)
Funktory
A funktor (zdefiniowany przez scalaz) jest jakimś "kontenerem" (używam terminu ekstremalnie luźno), F
takie, że jeśli mamy F[A]
i funkcję A => B
, to możemy dostać w swoje ręce F[B]
(pomyślmy na przykład F = List
i metodę map
)
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 z możliwością "podniesienia" funkcji A => B
do domeny funktora. Czyli:
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
To znaczy, jeśli F
jest funktorem, A Mamy funkcję A => B
, Mamy funkcję F[A] => F[B]
. Możesz spróbować i zaimplementuj metodę lift
- to dość trywialne.
Transformatory Monad
Jak mówi hcoopz poniżej (i właśnie zdałem sobie sprawę ,że uchroniłoby mnie to przed napisaniem Tony niepotrzebnego kodu), termin "lift" ma również znaczenie w Monad Transformers. Przypomnijmy, że transformatory monad są sposobem "układania" monad na siebie (monady się nie komponują).
Załóżmy więc, że masz funkcję, która zwraca IO[Stream[A]]
. To może być zamienione na transformator monad StreamT[IO, A]
. Teraz możesz chcieć "podnieść" jakąś inną wartość an IO[B]
być może do tego jest również StreamT
. Możesz to napisać:
StreamT.fromStream(iob map (b => Stream(b)))
Lub to:
iob.liftM[StreamT]
Nasuwa się pytanie: dlaczego chcę przekształcić IO[B]
w StreamT[IO, B]
?. Odpowiedzią byłoby "wykorzystanie 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]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-31 10:38:19
Innym zastosowaniem lifting , na który natknąłem się w dokumentach (niekoniecznie związanych ze scalą), jest przeciążenie funkcji z f: A -> B
z 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, czy {[5] } jest stosowany do pojedynczego elementu, czy do wielu elementów.
Tego rodzaju przeciążenia są często wykonywane deklaratywnie, np.
f: List[A] -> List[B]
f(xs) = f(xs(1)), f(xs(2)), ..., f(xs(n))
Lub
f: Set[A] -> Set[B]
f(xs) = \bigcup_{i = 1}^n f(xs(i))
Lub imperatywnie, np.
f: List[A] -> List[B]
f(xs) = xs map f
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-31 08:43:10
Zwróć uwagę, że każdy zbiór, który rozszerza PartialFunction[Int, A]
(jak wskazuje oxbow_lakes), może zostać zniesiony; tak na przykład
Seq(1,2,3).lift
Int => Option[Int] = <function1>
, która zamienia funkcję częściową w funkcję całkowitą, gdzie wartości nie zdefiniowane w zbiorze są odwzorowywane na None
,
Seq(1,2,3).lift(2)
Option[Int] = Some(3)
Seq(1,2,3).lift(22)
Option[Int] = None
Ponadto,
Seq(1,2,3).lift(2).getOrElse(-1)
Int = 3
Seq(1,2,3).lift(22).getOrElse(-1)
Int = -1
To pokazuje schludne podejście do unikania indeksu poza granicami WYJĄTKÓW.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-21 08:01:14
Istnieje równieżunlifting , który jest procesem odwrotnym do podnoszenia.
Jeśli lifting jest zdefiniowany jako
Przekształcenie funkcji częściowej
PartialFunction[A, B]
w całkowitą functionA => Option[B]
Wtedy unlifting jest
Przekształcenie funkcji całkowitej
A => Option[B]
w funkcję częściowąPartialFunction[A, B]
Biblioteka Standardowa Scala definiuje Function.unlift
as
def unlift[T, R](f: (T) ⇒ Option[R]): PartialFunction[T, R]
Na przykład Biblioteka play-json zapewnia unlift, aby pomóc z konstrukcją seriali JSON :
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class Location(lat: Double, long: Double)
implicit val locationWrites: Writes[Location] = (
(JsPath \ "lat").write[Double] and
(JsPath \ "long").write[Double]
)(unlift(Location.unapply))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-04 15:22:37