Scala Cats library for dummies — part 2

Idris Kabir
3 min readOct 4, 2016

--

Welcome to part 2 of my cat library for dummies, I encourage you read part 1 as this is some what a continuation. This article will be centered on the “Apply” type class, remember the “map” function in the Functor type class? its awesomeness was extends to the “Apply” type class too.

Since Apply extends the Functor type class and the Cartesian type class we pretty much have inherited function like “map” , “compose” e.t.c. However, Apply comes with a special function called “ap” which does something quite unique. It was originally called apply, but later renamed to ap. Below is the ap function signature and an explanation of what is means

def ap[A, B](ff: F[A => B])(fa: F[A]): F[B](ff: [A => B] ) — a function that transform a type F[A => B](fa : F[A]) — a data-type that take only one data-type e.g List, Future , OptionsF[B] — the final return type of the ap function, after applying the ff function to the fa data-type

by carefully looking at the ap signature you will notice two(2) distinct features from the map function in the Functor type class

  1. the ap transformation function is F[A => B] while in map the transformation function is simply A => B
  2. the parameter list are flipped, in map the data-type variable fa comes first before the transformation function f whereas in the ap function, the transformation function ff comes first before the data-type variable fa.

Let have a look that the ap function in action.

scala> import cats._ , cats.instances._ , cats.implicits._
import cats._
import cats.instances._
import cats.implicits._
scala> val addOne : Int => Int = _ + 1
addOne: Int => Int = <function1>
scala> Apply[List].ap(List(addOne))(List(1,2,3))
res0: List[Int] = List(2, 3, 4)
scala> Apply[Option].ap(Some(addOne))(Some(4))
res2: Option[Int] = Some(5)

some examples with less typing ;)

scala> (addOne).some ap 5.some
res3: Option[Int] = Some(6)
scala> (addOne).some ap None
res4: Option[Int] = None
scala> List(addOne) ap List(1,2,4)
res5: List[Int] = List(2, 3, 5)

the examples above applies for only function with a single parameter, what if we want to pass a function(our ff function) that takes 2 , 3 or more parameters ?

Fortunately, the Apply type class has variant of the ap function which are ap2 , ap3 until ap22.

ap2 means the ap function takes 2 parameters

scala> val addBoth=(a: Int , b: Int) => a + b 
addBoth: (Int, Int) => Int = <function2>
scala> (addBoth).some ap2 (3.some , 5.some)
res5: Option[Int] = Some(8)
scala> val append = (a: Int, b: List[Int]) => a :: b
b: (Int, List[Int]) => List[Int] = <function2>
scala> (append).some ap2 (4.some , List(2,3).some)
res8: Option[List[Int]] = Some(List(4, 2, 3))

such variants also exist for both map and tuple functions

map2 , map3 until map22.

tuple2 , tuple3 until tuple22.

The easy path with |@| .

When using the higher-arity functions(apN , mapN and tupleN) a neater alternative to use is called the “CartesianBuilder” with a symbol “ |@| ”, let try it out.

scala> (5.some |@| 6.some ).map(_ + _ ) 
res9: Option[Int] = Some(11)
scala>def addOptions( a: Option[Int], b: Option[Int])= (a |@| b).map(_+_)
add: (a: Option[Int], b: Option[Int])Option[Int]
scala> addOptions(5.some, 4.some)
res10: Option[Int] = Some(9)
scala> addOptions(None, 5.some)
res12: Option[Int] = None

Same can be applied to List and Future(don't forget to import the ExecutionContext)

scala> import scala.concurrent.Future
import scala.concurrent.Future
scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global
scala> def ff(a:Future[Int], b: Future[Int])= (a |@| b).map(_+_)
ff: (a: scala.concurrent.Future[Int], b: scala.concurrent.Future[Int])scala.concurrent.Future[Int]
scala> ff(Future.successful(34), Future.successful(56)).foreach(println)90

If you liked this writeup, click the💚 below so more people can see it here on Medium

will pulse here for now, hopefully part 3 will be on Applicative and Monad type class.

Big thanks to @julienrf , @zainab-ali and @yilinwei for taking the time to review this post.

--

--

Idris Kabir

Love Coding and talking about it , Scala enthusiastic, Abuja WeCode member, Lagos Scala Committee, CTO Zainpay.ng