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