首页 文章

什么是Scala中的“提升”?

提问于
浏览
229

有时,当我阅读Scala生态系统中的文章时,我读到了“提升”/“解除”一词 . 不幸的是,没有解释这究竟意味着什么 . 我做了一些研究,似乎提升与功能 Value 或类似的东西有关,但我无法找到一个文字,以初学者友好的方式解释实际提升的内容 .

通过Lift框架还有其他混淆,它的名称已经提升,但它无助于回答这个问题 .

What is "lifting" in Scala?

4 回答

  • 263

    有一些用法:

    PartialFunction

    请记住, PartialFunction[A, B] 是为域 A 的某个子集定义的函数(由 isDefinedAt 方法指定) . 你可以"lift" 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] = <function1>
    
    scala> pf.lift
    res1: Int => Option[Boolean] = <function1>
    
    scala> res1(-1)
    res2: Option[Boolean] = None
    
    scala> res1(1)
    res3: Option[Boolean] = Some(false)
    

    方法

    您可以"lift"将方法调用到函数中 . 这称为eta-expansion(感谢Ben James) . 例如:

    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
    

    注意方法和函数之间的根本区别 . res0 是(函数)类型的 instance (即它是一个值) (Int => Int)

    Functors

    一个仿函数(由 scalaz 定义)是"container"(我使用极松散的术语), F 这样,如果我们有一个 F[A] 和一个函数 A => B ,那么我们可以得到一个 F[B] (想想,例如, F = Listmap 方法)

    我们可以对此属性进行如下编码:

    trait Functor[F[_]] { 
      def map[A, B](fa: F[A])(f: A => B): F[B]
    }
    

    这是同形的,能够"lift"函数 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在下面所说(我刚刚意识到这会让我免于编写大量不必要的代码),术语"lift"在 Monad Transformers 中也有意义 . 回想一下,monad变换器是一种相互叠加的方式(monad不构成) .

    例如,假设您有一个返回 IO[Stream[A]] 的函数 . 这可以转换为monad变压器 StreamT[IO, A] . 现在你可能希望"lift"其他一些值 IO[B] 也许它也是 StreamT . 你可以这样写:

    StreamT.fromStream(iob map (b => Stream(b)))
    

    或这个:

    iob.liftM[StreamT]
    

    这引出了一个问题:为什么我要将 IO[B] 转换为 StreamT[IO, B] ?答案是"to take advantage of composition possibilities" . 假设你有一个函数 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]
    
  • 16

    我在论文中遇到的另一种解除用法(不一定是与Scala相关的)正在使用 f: List[A] -> List[B] (或集合,多集合,......)重载 f: A -> B 中的函数 . 这通常用于简化形式化,因为无论 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}^n f(xs(i))
    

    或者必要的,例如,

    f: List[A] -> List[B]
    f(xs) = xs map f
    
  • 19

    注意任何延伸 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
    

    这显示了一种简洁的方法来避免索引超出范围的异常 .

  • 5

    还有提升,这是提升的逆过程 .

    如果提升定义为

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

    然后解开是

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

    Scala标准库将Function.unlift定义为

    def unlift [T,R](f:(T)⇒选项[R]):PartialFunction [T,R]

    例如,play-json库提供unlift来帮助构建JSON serialisers

    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))
    

相关问题