Scala中的“提升”是什么?
有时当我阅读Scala生态系统中的文章时,我读到了“提升”/“提升”的术语。 不幸的是,这并没有解释到底是什么意思。 我做了一些研究,似乎解除与function价值或类似的东西有关,但我无法find一个文本,解释以新手友好的方式实际上提升。
Lift框架有一个额外的混淆,那就是它的名字已经提升了,但是它并没有帮助回答这个问题。
Scala中的“提升”是什么?
有几个用法:
PartialFunction
记住PartialFunction[A, B]
是为域A
某个子集定义的函数(由isDefinedAt
方法指定)。 您可以将PartialFunction[A, B]
“提升”为Function[A, Option[B]]
。 也就是说,一个定义在整个 A
的函数,但是其值是typesOption[B]
这是通过显式调用PartialFunction
上的方法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扩展 (感谢本詹姆斯为此)。 举个例子:
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 = 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
,我们有一个函数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 -> B
与f: 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]
然后是不举的
将总function
A => 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))