How lift function works?

Dear all,
I’,m reading the red book. here is an example code:

def lift[A,B](f: A => B): Option[A] => Option[B] = {
maybeA => maybeA map f
}
val absO: Option[Double] => Option[Double] = lift(math.abs)

How does it work? where the maybeA is come from with the type of Option?!

The return type of lift is a function and the implementation is just a lambda.

Is not different of saying: val f = (x: Int) => x + 1
Thus, maybeA is just the name that was given to the input of the function, and since we already told the compiler the return would be Option[A] => Option[B] then it knows that maybeA is a Option[A]

@BalmungSan Could u please write a non-lambda version to clarify it better?
you mean if we define the return type compiler do some magic and implement some code for us?
I mean we should create some Option types from A and B by ourselves. How does is work?

Not sure how would you write a non-lambda version.
We are returning a function, if you do not understand functions then you should not be reading the red book yet.

There is a lot of info about functions in the Scala Tour: Introduction | Tour of Scala | Scala Documentation


BTW, lift is not a function is a method, they are different.

1 Like

maybeA is the argument of the lambda function that is returned by the method lift.

Simpler example: consider val a = 1. Now, x => x + a would almost be a valid lambda, except that the argument type is missing. But if the type is provided by the context, it is fine, e.g.

val a = 1
(x => x + a): (Int => Int)

Alternatively:

val a = 1
val lam: Int => Int = x => x+a

Or, as the return value of a method:

def m(a: Int): Int => Int = x => x + a
2 Likes

I have read Martin’s book twice. I know something about Scala :wink:
but get confusing.
i will think about it and return again :wink:

thanks for your simplification :+1:

You haven’t mentioned it, so I will just clarify anyway. As you are reading books, ESPECIALLY the FP Red Book, I highly recommend you use the REPL, Scala Worksheet (available in both IntelliJ+Scala-Plugin or VSCode+Metals), and/or Scastie.

IOW, it is really important that you have direct concrete experience with these concepts. Trying to learn via just thinking and abstracting is just making things many more times difficult for yourself. And I am speaking from personal direct experience. You can save yourself many hours by playing in one of these tools directly with the Scala code. And, you will answer your own original question much more quickly.

3 Likes

Thanks for your friendly suggestion. yes REPL, intelliJ and i are good friends :wink:

1 Like

@BalmungSan @chaotic3quilibrium @curoli
Thanks guys, i’m getting functional :wink:

1 Like

Since A => B is special syntax for Function1[A, B], a non-lambda version would be explicitly writing a type that implements Function1[Option[A], Option[B]

def lift[A, B](f: A => B): Option[A] => Option[B] = new Function1[Option[A], Option[B]] {
  override def apply(maybeA: Option[A]): Option[B] = maybeA map f
}

You can see that maybeA maps to the name for the parameter to apply.

And that anonymous instance is isomorphic to a class that takes the function as a constructor parameter.


class Lift[-A, +B](f: A => B) extends Function1[Option[A], Option[B]] {
  override def apply(maybeA: Option[A]): Option[B] = maybeA map f
}

def lift[A, B](f: A => B): Option[A] => Option[B] = new Lift(f)

The lambda syntax maybeA => maybeA map f is therefore shorthand for the above, where the compiler generates the class and implementation and instantiates it for you, and possibly does something more efficient in cases where that’s possible.

2 Likes

thanks in advance