Function “什么是”呢;起重“;在斯卡拉?

Function “什么是”呢;起重“;在斯卡拉?,function,scala,functional-programming,lifting,Function,Scala,Functional Programming,Lifting,有时,当我阅读Scala生态系统中的文章时,我会读到术语“提升”/“提升”。不幸的是,没有解释这到底意味着什么。我做了一些研究,似乎提升与函数值或类似的东西有关,但我找不到一个文本以初学者友好的方式解释提升到底是什么 在这个名为提升的框架中存在着额外的混乱,但这无助于回答这个问题 什么是Scala中的“提升”?有几种用法: 部分功能 请记住部分函数[a,B]是为域a的某个子集定义的函数(由isDefinedAt方法指定)。您可以将部分功能[a,B]提升到功能[a,选项[B]]。也就是说,在整个a

有时,当我阅读Scala生态系统中的文章时,我会读到术语“提升”/“提升”。不幸的是,没有解释这到底意味着什么。我做了一些研究,似乎提升与函数值或类似的东西有关,但我找不到一个文本以初学者友好的方式解释提升到底是什么

在这个名为提升的框架中存在着额外的混乱,但这无助于回答这个问题


什么是Scala中的“提升”?

有几种用法:

部分功能 请记住
部分函数[a,B]
是为域
a
的某个子集定义的函数(由
isDefinedAt
方法指定)。您可以将
部分功能[a,B]
提升到
功能[a,选项[B]]
。也就是说,在整个
a
上定义的函数,但其值的类型为
选项[B]

这是通过在
PartialFunction
上显式调用方法
lift
完成的

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)
我们通过应用下划线将方法提升到函数中

这与能够将函数
A=>B
提升到函子的域中是同构的。即:

def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
也就是说,如果
F
是一个函子,我们有一个函数
a=>B
,我们就有一个函数
F[a]=>F[B]
。您可以尝试并实现
lift
方法-它非常简单

单子变压器 正如hcoopz在下面所说的(我刚刚意识到这可以避免我编写大量不必要的代码),术语“lift”在Monad Transformers中也有其含义。回想一下,单子变换器是一种将单子“堆叠”在彼此之上的方式(单子不组合)

例如,假设有一个函数返回一个
IO[Stream[a]]
。这可以转换为monad转换器
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]
lazy val f:(A,B)=>C=???
val cs=
为了{
a我在论文中遇到的提升的另一个用法(不一定是Scala相关的用法)是使用
f:a->B
f:List[a]->List[B]
(或集合、多集合等)重载函数。这通常用于简化形式化,因为无论是将
f
应用于单个元素还是多个元素,都无关紧要

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

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

或强制性地,例如

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

注意:任何扩展了
部分函数[Int,A]
(正如oxbow_lakes所指出的)的集合都可能被取消;例如

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

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

Seq(1,2,3).lift(22).getOrElse(-1)
Int = -1
这显示了一种避免索引越界异常的简洁方法。

还有取消提升,这是提升的相反过程

如果起重定义为:

将部分函数
部分函数[a,B]
转换为总计 函数
A=>选项[B]

那么,取消升空是必要的

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

Scala标准库定义为

例如,play json库提供了以下帮助:


这是oxbow_lakes描述的“提升为函子”。@BenJames的确如此。我的辩护是:当我开始写我的时,oxbow_lakes的答案还不存在。也许值得一提的是“将方法提升为函数”通常被称为eta-expansion。深入研究scalaz,提升也与monad transformers有关。如果我有一个
MonadTrans
实例
T
用于
M
和一个
monad
实例
N
,那么
T.liftM
可以用于提升
N[a]类型的值
转换为
M[N,a]类型的值
。谢谢Ben,hcoopz。我已经修改了answerPerfect!还有一个理由可以说:Scala是最好的。它可以被Martin Odersky&Co提升到最好的。我甚至想用
liftM
,但我不知道如何正确地做到这一点。伙计们,你们真是棒极了!在方法部分…res0是一个实例(即它是一个值)(函数)类型(Int=>Int)…不应该
f
是一个实例,而不是
res0
Seq(1,2,3).lift
Int => Option[Int] = <function1>
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
def unlift[T, R](f: (T) ⇒ Option[R]): PartialFunction[T, R]
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))