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]
MonadTrans
instanceT
pourM
et uneMonad
instance pourN
, alorsT.liftM
peut être utilisé pour soulever une valeur de typeN[A]
en une valeur de typeM[N, A]
.liftM
pour cela, mais je n'ai pas réussi à comprendre comment le faire correctement. Les gars, vous êtes rock!f
être une instance, nonres0
?Une autre utilisation de la levée que j'ai rencontrée dans des articles (pas nécessairement liés à Scala) est la surcharge d'une fonction
f: A -> B
avecf: List[A] -> List[B]
(ou ensembles, multisets, ...). Ceci est souvent utilisé pour simplifier les formalisations car il n'a alors pas d'importance s'ilf
est appliqué à un élément individuel ou à plusieurs éléments.Ce type de surcharge est souvent effectué de manière déclarative, par exemple,
ou
ou impérativement, par exemple,
la source
Notez que toute collection qui s'étend
PartialFunction[Int, A]
(comme indiqué par oxbow_lakes) peut être levée; ainsi par exemplequi transforme une fonction partielle en fonction totale dans laquelle des valeurs non définies dans la collection sont mappées
None
,De plus,
Cela montre une approche soignée pour éviter les exceptions d' index hors limites .
la source
Il y a aussi le déliftage , qui est le processus inverse de la levée.
Si le levage est défini comme
alors le décollage est
La bibliothèque standard Scala se définit
Function.unlift
commePar exemple, la bibliothèque play-json fournit unlift pour faciliter la construction des sérialiseurs JSON :
la source