Il y a quelques usages :
Fonction partielle
Souvenez-vous d'un PartialFunction[A, B]
est une fonction définie pour un certain sous-ensemble du domaine A
(comme spécifié par le isDefinedAt
). Vous pouvez "lever" un PartialFunction[A, B]
en un Function[A, Option[B]]
. C'est-à-dire qu'une fonction définie sur le todo de A
mais dont les valeurs sont de type Option[B]
Cela se fait par l'invocation explicite de la méthode lift
en 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)
Méthodes
Vous pouvez "lever" une invocation de méthode dans une fonction. Cela s'appelle eta-expansion (merci à Ben James pour cela). Ainsi, par exemple :
scala> def times2(i: Int) = i * 2
times2: (i: Int)Int
Nous transformons une méthode en une fonction en appliquant la fonction 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
es un instance (c'est-à-dire qu'il s'agit d'un valeur ) du type (fonction) (Int => Int)
Foncteurs
A foncteur (tel que défini par scalaz ) est un certain "conteneur" (j'utilise le terme extrêmement en vrac), F
de sorte que, si nous avons un F[A]
et une fonction A => B
alors nous pouvons mettre la main sur un F[B]
(pensez, par exemple, F = List
et le map
méthode)
Nous pouvons coder cette propriété comme suit :
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
Ceci est isomorphe au fait de pouvoir "lever" la fonction A => B
dans le domaine du foncteur. C'est-à-dire :
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
C'est-à-dire que si F
est un foncteur, et nous avons une fonction A => B
nous avons une fonction F[A] => F[B]
. Vous pourriez essayer de mettre en œuvre la lift
c'est assez trivial.
Transformateurs de monades
Comme hcoopz dit ci-dessous (et je viens de me rendre compte que cela m'aurait évité d'écrire une tonne de code inutile), le terme "ascenseur" a également une signification au sein de Transformateurs de monades . Rappelons que les transformateurs de monades sont un moyen d'"empiler" des monades les unes sur les autres (les monades ne se composent pas).
Par exemple, supposons que vous ayez une fonction qui renvoie un IO[Stream[A]]
. Ceci peut être converti en transformateur de monade StreamT[IO, A]
. Maintenant, vous pouvez souhaiter "lever" une autre valeur, un IO[B]
peut-être à cela qu'il est aussi un StreamT
. Vous pourriez soit écrire ceci :
StreamT.fromStream(iob map (b => Stream(b)))
Ou ça :
iob.liftM[StreamT]
cela pose la question : pourquoi je veux convertir un IO[B]
en un StreamT[IO, B]
? . La réponse serait "pour tirer parti des possibilités de composition". Disons que vous avez une fonction 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]