Il y a quelques utilisations:
Fonction partielle
N'oubliez pas que a PartialFunction[A, B]
est une fonction définie pour un sous-ensemble du domaine A
(comme spécifié par la isDefinedAt
méthode). Vous pouvez "soulever" un PartialFunction[A, B]
dans un Function[A, Option[B]]
. Autrement dit, une fonction définie sur l' ensemble de A
mais dont les valeurs sont de typeOption[B]
Cela se fait par l'invocation explicite de la méthode lift
sur 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)
Les méthodes
Vous pouvez "soulever" une invocation de méthode dans une fonction. C'est ce qu'on appelle eta-expansion (merci à Ben James pour cela). Ainsi, par exemple:
scala> def times2(i: Int) = i * 2
times2: (i: Int)Int
Nous élevons une méthode dans une fonction en appliquant le trait de soulignement
scala> val f = times2 _
f: Int => Int = <function1>
scala> f(4)
res0: Int = 8
Notez la différence fondamentale entre les méthodes et les fonctions. res0
est une instance (c'est-à-dire une valeur ) de type (fonction)(Int => Int)
Foncteurs
Un foncteur (tel que défini par scalaz ) est un "conteneur" (j'utilise le terme de façon très vague), de F
sorte que, si nous avons une F[A]
fonction et une A => B
, nous pouvons mettre la main sur unF[B]
(pensez, par exemple, F = List
et la map
méthode )
Nous pouvons encoder cette propriété comme suit:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
Ceci est isomorphe à la possibilité de "lever" la fonction A => B
dans le domaine du foncteur. C'est:
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
Autrement dit, si F
est un foncteur, et nous avons une fonction A => B
, nous avons une fonctionF[A] => F[B]
. Vous pourriez essayer d'implémenter la lift
méthode - c'est assez trivial.
Transformateurs de monade
Comme le dit hcoopz ci-dessous (et je viens de réaliser que cela m'aurait évité d'écrire une tonne de code inutile), le terme "lift" a également une signification dans Monad Transformers . Rappelons qu'un transformateur de monade est un moyen «d'empiler» les monades les unes sur les autres (les monades ne composent pas).
Par exemple, supposons que vous ayez une fonction qui renvoie un IO[Stream[A]]
. Celui-ci peut être converti en transformateur monade StreamT[IO, A]
. Maintenant, vous voudrez peut-être "soulever" une autre valeur et IO[B]
peut-être que c'est aussi un StreamT
. Vous pouvez soit écrire ceci:
StreamT.fromStream(iob map (b => Stream(b)))
Ou ca:
iob.liftM[StreamT]
cela soulève la question: pourquoi est-ce que je veux convertir un IO[B]
en un StreamT[IO, B]
? . La réponse serait "de profiter des possibilités de composition". Disons que vous avez une fonctionf: (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]