Scala中的“提升”是什么?

有时当我阅读Scala生态系统中的文章时,我读到了“提升”/“提升”的术语。 不幸的是,这并没有解释到底是什么意思。 我做了一些研究,似乎解除与function价值或类似的东西有关,但我无法find一个文本,解释以新手友好的方式实际上提升。

Lift框架有一个额外的混淆,那就是它的名字已经提升了,但是它并没有帮助回答这个问题。

Scala中的“提升”是什么?

有几个用法:

PartialFunction

记住PartialFunction[A, B]是为域A某个子集定义的函数(由isDefinedAt方法指定)。 您可以将PartialFunction[A, B] “提升”为Function[A, Option[B]] 。 也就是说,一个定义在整个 A的函数,但是其值是typesOption[B]

这是通过显式调用PartialFunction上的方法liftPartialFunction

 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) 

方法

您可以将一个方法调用“提升”到一个函数中。 这被称为eta扩展 (感谢本詹姆斯为此)。 举个例子:

 scala> def times2(i: Int) = i * 2 times2: (i: Int)Int 

我们通过应用下划线将一个方法提升为一个函数

 scala> val f = times2 _ f: Int => Int = <function1> scala> f(4) res0: Int = 8 

请注意方法和function之间的根本区别。 res0是(函数)types(Int => Int)一个实例 (即它是一个 (Int => Int)

函子

一个函数 (由scalaz定义)是一个“容器”(我使用这个术语非常松散), F ,如果我们有一个F[A]和一个函数A => B ,那么我们就可以得到我们的手F[B] (想想,例如, F = Listmap方法)

我们可以编码这个属性如下:

 trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] } 

这是同构的,能够将函数A => B “提升”到函子的域中。 那是:

 def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B] 

也就是说,如果F是一个函子,并且我们有一个函数A => B ,我们有一个函数F[A] => F[B] 。 你可以尝试和实施lift方法 – 这是相当微不足道的。

Monad变形金刚

正如hcoopz在下面说的(我刚刚意识到这样可以使我避免编写大量不必要的代码),在“ Monad Transformers ”中,“lift”这个术语也是有意义的。 回想一下,monad变压器是将monad“堆叠”在一起的一种方式(monad不能组成)。

所以举个例子,假设你有一个返回IO[Stream[A]]的函数。 这可以转换成单stream变换器StreamT[IO, A] 。 现在你可能希望“提升”一些IO[B]价值,也许它也是一个StreamT 。 你可以写这个:

 StreamT.fromStream(iob map (b => Stream(b))) 

或这个:

 iob.liftM[StreamT] 

这引出了一个问题: 为什么我要将IO[B]转换为StreamT[IO, B] 。 答案是“利用组合的可能性”。 假设你有一个函数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] 

我在文件中遇到的另一个提升的用法(不一定是与Scala相关的)使用f: A -> Bf: List[A] -> List[B] (或者sets,multisets, …)。 这通常用于简化forms化,因为f是应用于单个元素还是应用于多个元素并不重要。

这种重载通常是以声明的方式完成的,例如,

 f: List[A] -> List[B] f(xs) = f(xs(1)), f(xs(2)), ..., f(xs(n)) 

要么

 f: Set[A] -> Set[B] f(xs) = \bigcup_{i = 1}^nf(xs(i)) 

或者势在必行,例如,

 f: List[A] -> List[B] f(xs) = xs map f 

请注意,任何扩展了PartialFunction[Int, A] (正如oxbow_lakes指出的)的集合可能会被取消; 因此例如

 Seq(1,2,3).lift Int => Option[Int] = <function1> 

这将部分函数转化为一个总函数,其中未在集合中定义的值映射到None

 Seq(1,2,3).lift(2) Option[Int] = Some(3) Seq(1,2,3).lift(22) Option[Int] = None 

此外,

 Seq(1,2,3).lift(2).getOrElse(-1) Int = 3 Seq(1,2,3).lift(22).getOrElse(-1) Int = -1 

这显示了避免索引越界exception的简洁方法。

还有一个解除 ,这是解除的反过程。

如果提升被定义为

将部分函数PartialFunction[A, B]转换为总函数A => Option[B]

然后是不举的

将总functionA => Option[B]转换为部分functionPartialFunction[A, B]

Scala标准库定义Function.unlift

 def unlift[T, R](f: (T) ⇒ Option[R]): PartialFunction[T, R] 

例如,play-json库提供unlift来帮助构buildJSON串行器 :

 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))