什么是 Scala 中的“提升”?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17965059/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is "lifting" in Scala?
提问by user573215
Sometimes when I read articles in the Scala ecosystem I read the term "lifting" / "lifted". Unfortunately, it is not explained what that exactly means. I did some research, and it seems that lifting has something to do with functional values or something like that, but I was not able to find a text that explains what lifting actually is about in a beginner friendly way.
有时,当我阅读 Scala 生态系统中的文章时,我会读到“提升”/“提升”这个词。不幸的是,没有解释这究竟意味着什么。我做了一些研究,似乎提升与功能价值或类似的东西有关,但我无法找到以初学者友好的方式解释提升实际上是什么的文本。
There is additional confusion through the Liftframework which has lifting in its name, but it doesn't help answer the question.
Lift框架中还有其他混淆,它的名称中包含提升,但它无助于回答这个问题。
What is "lifting" in Scala?
什么是 Scala 中的“提升”?
回答by oxbow_lakes
There are a few usages:
有几种用法:
PartialFunction
偏函数
Remember a PartialFunction[A, B]is a function defined for some subset of the domain A(as specified by the isDefinedAtmethod). You can "lift" a PartialFunction[A, B]into a Function[A, Option[B]]. That is, a function defined over the wholeof Abut whose values are of type Option[B]
记住 aPartialFunction[A, B]是为域的某个子集定义的函数A(由isDefinedAt方法指定)。您可以将 a “提升”PartialFunction[A, B]为 a Function[A, Option[B]]。也就是说,一个在整个of 上定义的函数,A但其值的类型是Option[B]
This is done by the explicit invocation of the method lifton PartialFunction.
这是通过显式调用lifton的方法来完成的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)
Methods
方法
You can "lift" a method invocation into a function. This is called eta-expansion(thanks to Ben James for this). So for example:
您可以将方法调用“提升”为函数。这称为eta-expansion(感谢 Ben James)。例如:
scala> def times2(i: Int) = i * 2
times2: (i: Int)Int
We lift a method into a function by applying the underscore
我们通过应用下划线将方法提升为函数
scala> val f = times2 _
f: Int => Int = <function1>
scala> f(4)
res0: Int = 8
Note the fundamental difference between methods and functions. res0is an instance(i.e. it is a value) of the (function) type (Int => Int)
请注意方法和函数之间的根本区别。res0是(函数)类型的一个实例(即它是一个值)(Int => Int)
Functors
函子
A functor(as defined by scalaz) is some "container" (I use the term extremelyloosely), Fsuch that, if we have an F[A]and a function A => B, then we can get our hands on an F[B](think, for example, F = Listand the mapmethod)
一个函子(由scalaz定义)是一些“容器”(我使用这个术语非常松散),F这样,如果我们有一个F[A]和一个函数A => B,那么我们可以得到一个F[B](例如,想想,F = List和map方法)
We can encode this property as follows:
我们可以对这个属性进行如下编码:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
This is isomorphic to being able to "lift" the function A => Binto the domain of the functor. That is:
这与能够将函数“提升”A => B到函子的域中是同构的。那是:
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
That is, if Fis a functor, and we have a function A => B, we have a function F[A] => F[B]. You might try and implement the liftmethod - it's pretty trivial.
也就是说,如果F是一个函子,而我们有一个函数A => B,我们就有一个函数F[A] => F[B]。您可以尝试实施该lift方法 - 这非常简单。
Monad Transformers
Monad 变形金刚
As hcoopzsays below (and I've just realized that this would have saved me from writing a ton of unnecessary code), the term "lift" also has a meaning within Monad Transformers. Recall that a monad transformers are a way of "stacking" monads on top of each other (monads do not compose).
正如hcoopz在下面所说的(我刚刚意识到这会让我免于编写大量不必要的代码),术语“lift”在Monad Transformers 中也有含义。回想一下,monad 转换器是一种将 monad 彼此“堆叠”的方式(monad 不构成)。
So for example, suppose you have a function which returns an IO[Stream[A]]. This can be converted to the monad transformer StreamT[IO, A]. Now you may wish to "lift" some other value an IO[B]perhaps to that it is also a StreamT. You could either write this:
例如,假设您有一个返回IO[Stream[A]]. 这可以转换为 monad 转换器StreamT[IO, A]。现在你可能希望“提升”一些其他的价值,IO[B]也许它也是一个StreamT. 你可以这样写:
StreamT.fromStream(iob map (b => Stream(b)))
Or this:
或这个:
iob.liftM[StreamT]
this begs the question: why do I want to convert an IO[B]into a StreamT[IO, B]?. The answer would be "to take advantage of composition possibilities". Let's say you have a function f: (A, B) => C
这就引出了一个问题:为什么我要将 an 转换IO[B]为 a 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]
回答by Malte Schwerhoff
Another usage of liftingthat I've come across in papers (not necessarily Scala-related ones) is overloading a function from f: A -> Bwith f: List[A] -> List[B](or sets, multisets, ...). This is often used to simplify formalisations because it then doesn't matter whether fis applied to an individual element or to multiple elements.
我在论文(不一定是与 Scala 相关的那些)中遇到的另一个提升的用法是从f: A -> Bwith f: List[A] -> List[B](或集合、多重集合,...)重载一个函数。这通常用于简化形式化,因为无论f是应用于单个元素还是多个元素都无关紧要。
This kind of overloading is often done declaratively, e.g.,
这种重载通常以声明方式完成,例如,
f: List[A] -> List[B]
f(xs) = f(xs(1)), f(xs(2)), ..., f(xs(n))
or
或者
f: Set[A] -> Set[B]
f(xs) = \bigcup_{i = 1}^n f(xs(i))
or imperatively, e.g.,
或强制地,例如,
f: List[A] -> List[B]
f(xs) = xs map f
回答by elm
Note any collection that extends PartialFunction[Int, A](as pointed out by oxbow_lakes) may be lifted; thus for instance
请注意,任何扩展的集合PartialFunction[Int, A](如 oxbow_lakes 所指出的)都可能被解除;因此例如
Seq(1,2,3).lift
Int => Option[Int] = <function1>
which turns a partial function into a total function where values not defined in the collection are mapped onto None,
它将部分函数转换为一个总函数,其中集合中未定义的值被映射到None,
Seq(1,2,3).lift(2)
Option[Int] = Some(3)
Seq(1,2,3).lift(22)
Option[Int] = None
Moreover,
而且,
Seq(1,2,3).lift(2).getOrElse(-1)
Int = 3
Seq(1,2,3).lift(22).getOrElse(-1)
Int = -1
This shows a neat approach to avoid index out of boundsexceptions.
这显示了一种避免索引越界异常的巧妙方法。
回答by Mario Galic
There is also unlifting, which is the inverse process to lifting.
还有unlifting,这是lifting的逆过程。
If lifting is defined as
如果提升定义为
turning a partial function
PartialFunction[A, B]into a total functionA => Option[B]
将偏函数
PartialFunction[A, B]转化为全函数A => Option[B]
then unlifting is
然后解除是
turning a total function
A => Option[B]into a partial functionPartialFunction[A, B]
将全函数
A => Option[B]转化为偏函数PartialFunction[A, B]
Scala standard library defines Option[R]):PartialFunction[T,R]" rel="noreferrer">Function.unliftas
Scala 标准库定义Option[R]):PartialFunction[T,R]" rel="noreferrer">Function.unlift为
def unlift[T, R](f: (T) ? Option[R]): PartialFunction[T, R]
def unlift[T, R](f: (T) ? Option[R]): PartialFunction[T, R]
For example, play-json library provides unliftto help with construction of JSON serialisers:
例如, play-json 库提供unlift来帮助构建JSON 序列化器:
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))

