什么是Scala中的“提升”?

时间:2013-07-31 08:16:24

标签: function scala functional-programming lifting

有时当我阅读Scala生态系统中的文章时,我会读到“提升”/“提升”一词。不幸的是,没有解释这究竟意味着什么。我做了一些研究,似乎提升与功能价值或类似的东西有关,但我无法找到一个文本,以初学者友好的方式解释实际提升的内容。

通过Lift框架引起了额外的混淆,其名称已经解除,但它无助于回答这个问题。

Scala中的“提升”是什么?

4 个答案:

答案 0 :(得分:274)

有一些用法:

PartialFunction

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

这是通过在lift上显式调用方法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)

方法

您可以将方法调用“提升”到函数中。这被称为 eta-expansion (感谢Ben James)。例如:

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

注意方法和功能之间的根本区别。 res0是(功能)类型(Int => Int)

实例(即

函子

仿函数(由 scalaz 定义)是一些“容器”(我使用术语松散地),{{1}这样,如果我们有一个F和一个F[A]函数,那么我们可以得到A => B(例如,F[B]和{{ 1}}方法)

我们可以按如下方式对此属性进行编码:

F = List

这是同形的,能够将函数map“提升”到仿函数的域中。那就是:

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方法 - 这非常简单。

Monad变形金刚

正如下面的 hcoopz 所说(我刚刚意识到这会让我免于编写大量不必要的代码),术语“提升”在 Monad中也有意义变压器即可。回想一下,monad变换器是一种将monad“叠加”在一起的方式(monad不构成)。

例如,假设您有一个返回F[A] => F[B]的函数。这可以转换为monad变换器lift。现在你可能希望将一些其他值“提升”IO[Stream[A]],或许它也是StreamT[IO, A]。你可以这样写:

IO[B]

或者这个:

StreamT

这引出了一个问题:为什么我要将StreamT.fromStream(iob map (b => Stream(b))) 转换为iob.liftM[StreamT] 。答案是“利用构图可能性”。假设你有一个函数IO[B]

StreamT[IO, B]

答案 1 :(得分:20)

我在论文中遇到的提升的另一种用法(不一定是Scala相关的)正在使用f: A -> B f: List[A] -> List[B]重载函数(或集合, multisets,...)。这通常用于简化形式化,因为无论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}^n f(xs(i))

或命令性地,例如,

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

答案 2 :(得分:17)

注意任何延伸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

这显示了一种简洁的方法来避免索引越界异常。

答案 3 :(得分:5)

还有 unlifting ,这是提升的逆过程。

如果提升定义为

  

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

然后放松是

  

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

Scala标准库将Function.unlift定义为

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

例如,play-json库提供unlift来帮助构建JSON serialisers

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