"lifting"在Scala中是什么意思?
"lifting"在Scala中是什么意思?
有时在Scala生态系统中阅读文章时,会看到术语“lifting” / “lifted”。不幸的是,它没有解释那究竟是什么意思。我做了一些研究,似乎lifting与函数值或类似于此的东西有关,但我没有找到一个以初学者友好的方式解释lifting是关于什么的文本。
由于Lift框架中有lifting,这使得情况更加混乱,但它并不能回答这个问题。
在Scala中,“lifting”是什么?
注意到任何扩展PartialFunction[Int, A]
的集合都可以被提升;因此例如
Seq(1,2,3).lift Int => Option[Int] =
可以将一个局部函数转换为总函数,其中在集合中没有定义的值映射到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
这表明了一种简洁的方法来避免索引超出边界异常。
有几种用法:
PartialFunction
记住,PartialFunction[A, B]
是一种针对域 A
的子集定义的函数(由 isDefinedAt
方法指定)。您可以将一个 PartialFunction[A,B]
提升为一个 Function[A,Option[B]]
。也就是说,它是在整个域 A
上定义的函数,但其值的类型为 Option[B]
这是通过对 PartialFunction
进行显式调用 lift
方法实现的。
scala> val pf: PartialFunction[Int, Boolean] = { case i if i > 0 => i % 2 == 0} pf: PartialFunction[Int,Boolean] =scala> pf.lift res1: Int => Option[Boolean] = scala> res1(-1) res2: Option[Boolean] = None scala> res1(1) res3: Option[Boolean] = Some(false)
方法
您可以将方法调用提升为一种函数。这称为 eta-扩展(感谢 Ben James),例如:
scala> def times2(i: Int) = i * 2 times2: (i: Int)Int
我们通过应用下划线将方法提升为一个函数
scala> val f = times2 _ f: Int => Int =scala> f(4) res0: Int = 8
请注意方法和函数之间的根本区别。 res0
是 (函数) 类型 (Int => Int)
的一个实例(即它是一个值)
Functors
Functor(由 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
提升到 Functor 的域中。也就是说:
def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]
也就是说,在 functor F
存在,并且我们有函数 A => B
,我们有函数 F[A] => F[B]
。您可以尝试实现 lift
方法 - 它非常简单。
Monad Transformers
正如下面的hcoopz所说(我刚意识到这将节省我写大量不必要的代码),“lift”这个术语在Monad Transformers中也有意义。回想一下,monad transformers是一种在彼此之上“堆叠”monad的方法(monad不能组合)。
例如,假设你有一个返回IO[Stream[A]]
的函数。这可以转换为monad transformerStreamT[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]