In my previous blog post about categories and functors I already threatened you with the possibility of a follow-up. Well, here we go. Actually I won’t talk about category theory this time, but about an important abstraction from the world of advanced (at least in my understanding) functional programming which turns out to be a close relative to the now well understood functor.

## Functors

Just in case you don’t remember exactly what a functor looks like, here comes the definition:

trait GenericFunctor[->>[_, _], ->>>[_, _], F[_]] { def fmap[A, B](f: A ->> B): F[A] ->>> F[B] } trait Functor[F[_]] extends GenericFunctor[Function, Function, F] { final def fmap[A, B](as: F[A])(f: A => B): F[B] = fmap(f)(as) }

For the sake of simplicity we will stick to the more specific *Functor* definition throughout the rest of this post. Such a functor is an endofunctor, because its source and target are the same (the category of Scala types and Scala functions). Maybe you remember that such a functor can be regarded as a provider of a computational context: The function *f: A => B* you give to *fmap* is lifted into the functor’s context which means that it is executed (maybe once, maybe several times or maybe even not at all) under the control of the functor.

As an example let’s look at how the *OptionFunctor* defined in the previous blog post is working: If we give *fmap* a *Some* the given function will be invoked, if we give it a *None* it won’t be invoked.

scala> fmap(Option(1)) { x => println("I was invoked!"); x + 1 } I was invoked! res0: Option[Int] = Some(2) scala> fmap(None: Option[Int]) { x => println("I was invoked!"); x + 1 } res1: Option[Int] = None

So far, so good. Using functors we can lift functions of **arity-1** into a computational context.

## Applicatives

But what if we have a function of higher arity? Can we still use a functor to lift a function of, let’s say, **arity-2**?

Let’s look at what happens if we call *fmap* to partially apply an arity-2 function to its first argument within the computational context of an *Option*:

scala> val f = (x: Int) => (y: Int) => x + y + 10 f: (Int) => (Int) => Int = <function1> scala> fmap(Option(1))(f) res0: Option[(Int) => Int] = Some()

What we get back is an *Option[Int => Int]*, i.e. the “rest” of the partially applied function wrapped in an *Option*. Now we have a problem, because we cannot give this lifted function to another call of *fmap*.

scala> fmap(Option(2))(fmap(Option(1))(f)) :13: error: type mismatch; found : Option[(Int) => Int] required: (Int) => ? fmap(Option(2))(fmap(Option(1))(f))

Of course we cannot, because *fmap* expects a pure function, not a lifted one. And that’s the moment when applicatives enter the stage. The idea is simple and follows intutively from what we have just seen: Instead of *fmap* taking a pure function, an *Applicative* defines the method *apply* taking a lifted function. And it defines the method *pure* to lift pure functions. Using these it is perfectly possible to partially apply an arity-n function to all of its arguments within a computational context. Before we look at our example from this new perspective, let’s code up our new abstraction:

trait Applicative[F[_]] extends Functor[F] { def pure[A](a: A): F[A] def apply[A, B](f: F[A => B]): F[A] => F[B] final def apply[A, B](fa: F[A])(f: F[A => B]): F[B] = apply(f)(fa) override def fmap[A, B](f: A => B): F[A] => F[B] = apply(pure(f)) }

As you can see, there is a strong relation between functors and applicatives: Each applicative is a functor and by one of the laws for applicatives the following has to hold true: *fmap = apply ο pure*. Well, this law is pretty intuitive, because it makes sure we can use an applicative as a functor, i.e. for a pure arity-1 function, and it will behave as expected.

In order to be able to work with applicatives we need some scaffolding and for our *Option* example we need an implementation of *Applicative*:

object Applicative { def pure[A, F[_]](a: A)(implicit applicative: Applicative[F]): F[A] = applicative pure a def apply[A, B, F[_]](fa: F[A])(f: F[A => B])(implicit applicative: Applicative[F]): F[B] = applicative.apply(fa)(f) implicit object OptionApplicative extends Applicative[Option] { override def pure[A](a: A): Option[A] = Option(a) override def apply[A, B](f: Option[A => B]): Option[A] => Option[B] = o => for { a <- o; p <- f } yield p(a) } }

Now let’s look at our example from above with an arity-2 function. Using a functor we got stuck, but using an applicative we can make it all the way through the two arguments:

scala> apply(Option(1))(apply(Option(2))(pure(f))) res0: Option[Int] = Some(13)

Ain’t that nice? You might answer, that this code looks cumbersome and is not necessary at all. Regarding the first argument please read on. Regarding the second: Yes, of course we don’t need an applicative for *Option*, because it already offers *flatMap* which does the job. But with this type class approach we can deal with any class, e.g. with *Either* from the Scala standard library or with classes from our own projects.

## Conclusion

You have seen the basic principle of applicatives: We can apply functions of arbitrary arity (well, greater or even one) to its arguments within a computational context. As functors provide exactly this for arity-1, applicatives are generalized functors.

Thanks to Scala’s flexibility we can of course do much better than above. Using a little pimp my library and some operators we can get something that’s elegant and useful. Luckily the scalaz folks have already done this, so I will just show two ways of expressing the above example using this awesome library:

scala> Option(1) <*> (Option(2) <*> Option(f)) res0: Option[Int] = Some(13) scala> (Option(1) <**> Option(2)) { _ + _ + 10 } res1: Option[Int] = Some(13)

Now this is really nice, isn’t it? Especially the second one looks very concise and intuitive. And as I stated above we can use it for types that don’t bring helpful methods like *flatMap*. Let’s conclude with another example using *Either* which is a perfect candidate to be used for results that might fail with well defined errors:

scala> (1.right[String] <**> 2.right[String]) { _ + _ + 10 } res0: Either[String,Int] = Right(13) scala> (1.right[String] <**> "Error".left[Int]) { _ + _ + 10 } res1: Either[String,Int] = Left(Error)

If you are interested in the code examples shown above, you can find these here: https://github.com/weiglewilczek/fun