Es gibt einige Verwendungen:
PartialFunction
Denken Sie daran, dass a PartialFunction[A, B]
eine Funktion ist, die für eine Teilmenge der Domäne definiert ist A
(wie von der isDefinedAt
Methode angegeben). Sie können ein PartialFunction[A, B]
in ein "heben" Function[A, Option[B]]
. Das heißt, eine Funktion über die definierte ganze von A
aber , deren Werte vom TypOption[B]
Dies erfolgt durch den expliziten Aufruf der Methode lift
am 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)
Methoden
Sie können einen Methodenaufruf in eine Funktion "heben". Dies nennt man eta-Expansion (danke an Ben James dafür). Also zum Beispiel:
scala> def times2(i: Int) = i * 2
times2: (i: Int)Int
Wir heben eine Methode in eine Funktion auf, indem wir den Unterstrich anwenden
scala> val f = times2 _
f: Int => Int = <function1>
scala> f(4)
res0: Int = 8
Beachten Sie den grundlegenden Unterschied zwischen Methoden und Funktionen. res0
ist eine Instanz (dh ein Wert ) vom Typ (Funktions-)(Int => Int)
Funktoren
Ein Funktor (wie von scalaz definiert ) ist ein "Container" (ich verwende den Begriff extrem locker), F
so dass wir, wenn wir eine F[A]
und eine Funktion haben A => B
, einen in die Hände bekommen können F[B]
(denken Sie zum Beispiel an F = List
die map
Methode) )
Wir können diese Eigenschaft wie folgt codieren:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
Dies ist isomorph, um die Funktion A => B
in die Domäne des Funktors "heben" zu können . Das ist:
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
Das heißt, wenn F
es sich um einen Funktor handelt und wir eine Funktion haben A => B
, haben wir eine Funktion F[A] => F[B]
. Sie könnten versuchen, die lift
Methode zu implementieren - sie ist ziemlich trivial.
Monadentransformatoren
Wie hcoopz weiter unten sagt (und ich habe gerade festgestellt, dass ich dadurch nicht eine Menge unnötigen Codes geschrieben hätte), hat der Begriff "Lift" auch innerhalb von Monad Transformers eine Bedeutung . Denken Sie daran, dass Monadentransformatoren eine Möglichkeit sind, Monaden übereinander zu "stapeln" (Monaden komponieren nicht).
Angenommen, Sie haben eine Funktion, die eine zurückgibt IO[Stream[A]]
. Dies kann in den Monadentransformator umgewandelt werden StreamT[IO, A]
. Jetzt möchten Sie vielleicht einen anderen Wert "anheben" und IO[B]
vielleicht ist es auch ein StreamT
. Sie könnten entweder Folgendes schreiben:
StreamT.fromStream(iob map (b => Stream(b)))
Oder dieses:
iob.liftM[StreamT]
das wirft die frage auf: warum möchte ich ein IO[B]
in ein umwandeln StreamT[IO, B]
? . Die Antwort wäre "Kompositionsmöglichkeiten nutzen". Angenommen, Sie haben eine Funktionf: (A, B) => C
lazy val f: (A, B) => C = ???
val cs =
for {
a <- as
b <- bs.liftM[StreamT]
}
yield f(a, b)
cs.toStream
MonadTrans
BeispielT
fürM
und einMonad
Beispiel fürN
, dannT.liftM
kann verwendet werden , um hebt einen Wert vom TypN[A]
auf einen Wert von TypM[N, A]
.liftM
, habe aber nicht verstanden, wie man das richtig macht. Leute, du bist Rock!f
Instanz sein, nichtres0
?Beachten Sie, dass jede Sammlung, die erweitert wird
PartialFunction[Int, A]
(wie von oxbow_lakes hervorgehoben), aufgehoben werden kann. so zum BeispielSeq(1,2,3).lift Int => Option[Int] = <function1>
Dadurch wird aus einer Teilfunktion eine Gesamtfunktion, auf die Werte abgebildet werden
None
, die nicht in der Sammlung definiert sind .Seq(1,2,3).lift(2) Option[Int] = Some(3) Seq(1,2,3).lift(22) Option[Int] = None
Außerdem,
Seq(1,2,3).lift(2).getOrElse(-1) Int = 3 Seq(1,2,3).lift(22).getOrElse(-1) Int = -1
Dies zeigt einen übersichtlichen Ansatz, um Ausnahmen außerhalb des Index zu vermeiden .
quelle
Eine andere Verwendung des Hebens , auf die ich in Zeitungen gestoßen bin (nicht unbedingt auf Scala-bezogene), ist das Überladen einer Funktion
f: A -> B
mitf: List[A] -> List[B]
(oder Mengen, Multisets, ...). Dies wird häufig verwendet, um Formalisierungen zu vereinfachen, da es dann nicht darauf ankommt, obf
es auf ein einzelnes Element oder auf mehrere Elemente angewendet wird.Diese Art der Überladung erfolgt häufig deklarativ, z.
f: List[A] -> List[B] f(xs) = f(xs(1)), f(xs(2)), ..., f(xs(n))
oder
f: Set[A] -> Set[B] f(xs) = \bigcup_{i = 1}^n f(xs(i))
oder unbedingt, z.
f: List[A] -> List[B] f(xs) = xs map f
quelle
Es gibt auch ein Aufheben , was der umgekehrte Vorgang zum Anheben ist.
Wenn das Heben definiert ist als
dann ist das Aufheben
Scala Standardbibliothek definiert
Function.unlift
alsZum Beispiel bietet die play-json-Bibliothek Unlift , um beim Aufbau von JSON-Serialisierern zu helfen :
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))
quelle