什么是 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 05:33:59  来源:igfitidea点击:

What is "lifting" in Scala?

functionscalafunctional-programminglifting

提问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 = Listmap方法)

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 function A => Option[B]

将偏函数PartialFunction[A, B]转化为全函数A => Option[B]

then unlifting is

然后解除是

turning a total function A => Option[B]into a partial function PartialFunction[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))