As I was working through some content on the Reader monoid, I realized I wanted to lift a function to use it. If you read the Scala In Depth book, which as a really great book, you know that lifting a function to be monoidal could be useful.
The classic example is to lift the function so that the arguments as well as the return value are all Option types. Then, if any of the parameters to the new lifted function are None, the function returns None. Otherwise it would return Some.
The key thought is that you do not have to write your own, scalaz already supports lift.
It turns out that scalaz has support for lifting functions using your monoid of choice. Below is a transcript using scala REPL with a -cp set to the scalaz core library.
The easiest approach is to use optionInstance.lift(func2)
where func2 takes 2 arguments.
Where does optionInstance come from? The scalaz.std
package contains objects (modules really) that contain a variety of nice constructors and methods implicitly available. So there is an object scalaz.std.option that contains an object called optionInstance. So there is an object called scalaz.std.option
which contains other vals or objects. If you are managing your import scope well, import scalaz.std.option.optionInstance
. The std
in scalaz refers to scala's standard library. For Either, there is a an object scalaz.std.either
which has a scalaz.std.either.eitherInstance
as well. The mnemonic is that by importing the optionInstance, you are bringing in constructive implicits which automatically wrap your type into scalaz objects when needed. optionInstance will also contain type classes for calling methods on type classes directly such as we did with Monad[Option] below.
$ scala -cp ./scalaz-core_2.10-7.1.0-M2.jar
Welcome to Scala version 2.10.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scalaz._
import scalaz._
scala> import std.option._
import std.option._
scala> def func2(i: Int, s: String) = s + "-" + i
func2: (i: Int, s: String)String
scala> func2(10, "base")
res0: String = base-10
// Messy with the type signature, you do not actually need it.
scala> def func2a = Apply[Option].apply2(_: Option[Int], _: Option[String])(func2)
func2a: (Option[Int], Option[String]) => Option[String]
scala> func2a(Some(10), Some("blah"))
res1: Option[String] = Some(blah-10)
scala> func2a(Some(10), Some("base"))
res2: Option[String] = Some(base-10)
scala> func2a(Some(10), None)
res3: Option[String] = None
scala> def func2b = Applicative[Option].lift2(func2)
func2c: (Option[Int], Option[String]) => Option[String]
scala> func2b(Some(10), Some("baseline"))
res12: Option[String] = Some(baseline-10)
scala> func2b(Some(10), None)
res13: Option[String] = None
scala> def func2d = Apply[Option].lift2(func2)
func2d: (Option[Int], Option[String]) => Option[String]
scala> func2d(Some(10), None)
res20: Option[String] = None
scala> func2d(Some(10), Some("blah"))
res21: Option[String] = Some(blah-10)
scala> def func2e = Monad[Option].lift2(func2)
func2e: (Option[Int], Option[String]) => Option[String]
scala> func2e(Some(10), Some("blah"))
res34: Option[String] = Some(blah-10)
// The easiest approach of them all!
scala> optionInstance.lift(func)
res2: Option[Int] => Option[String] = <function1>
Having describe how to generally lift a function, for example, the above code lifts func or func2 into an Option monad (we also call this lifting a function into an option environment), there are already builtin functions for lifting when using, for example, applicative syntax in scalaz. For example, the below code does the lifting on the function for us so we do not have to lift it ourselves. Note that we could use the applicative style here which is different than a for-comprehension. The applicative style is least powerful abstraction to use and that's a good design choice usually.
// Typing this off the top of my head
(AfuncReturingOptionInt |@| BfuncReturningOptionString) ( func2 )
Here, func2 is not lifted, it is lifted for us in the applicative implementation.