Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
621 views
in Technique[技术] by (71.8m points)

function - What is "lifting" in Scala?

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.

There is additional confusion through the Lift framework which has lifting in its name, but it doesn't help answer the question.

What is "lifting" in Scala?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 isDefinedAt method). You can "lift" a PartialFunction[A, B] into a Function[A, Option[B]]. That is, a function defined over the whole of A but whose values are of type Option[B]

This is done by the explicit invocation of the method lift on 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:

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. res0 is an instance (i.e. it is a value) of the (function) type (Int => Int)

Functors

A functor (as defined by scalaz) is some "container" (I use the term extremely loosely), F such 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 = List and the map method)

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 => B into the domain of the functor. That is:

def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]

That is, if F is a functor, and we have a function A => B, we have a function F[A] => F[B]. You might try and implement the lift method - it's pretty trivial.

Monad Transformers

As hcoopz says 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).

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:

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

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]

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...