Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does "ap" of \/ in Scalaz do?

Tags:

scala

scalaz

I am looking at disjunction type of scalaz and I noticed method ap

  /** Apply a function in the environment of the right of this disjunction. */
  def ap[AA >: A, C](f: => AA \/ (B => C)): (AA \/ C) =
    f flatMap (ff => map(ff(_)))

I guess I understand what it does. Now I wonder when and why one should actually use it ? Are there any examples of using this ap function ?

like image 323
Michael Avatar asked Mar 18 '14 15:03

Michael


1 Answers

The disjunction you are looking for:

import scalaz.{ \/, -\/ \/-, EitherT }
import scalaz.syntax.ToIdOps

object Testing extends ToIdOps // left and right methods come from there {
  // say you have the following method
  def someMethod(flag: Boolean): \/[Exception, SomeObject] {
    if (flag) someObj.right else new Exception("this is a sample").left
  }
}

// pattern matching
val x = someMethod match {
  case \/-(right) => // this is someObject
  case -\/(err) => // deal with the error  
}

// catamorphism
def methodThatDealsWithObj(obj: someObject)
def methodThatDealsWithErr(err: Exception)
someMethod.fold(methodThatDealsWithObj)(methodThatDealsWithErr)

// for comprehensions
// ap behaves just like EitherT.
for {
  correctResponse <- EitherT(someMethod)
}

Update

To understand how EitherT and ap works, think of an Option, which has Some and None and potential matches. With an Option, you would do:

for {
  a <- someOption
} yield ..

With scalaz.\/, you usually put an Exception on the left and a "correct" return type on the right. ap is a function that says apply this if the either has the correct type.

for {
  correctResponse <- ap(someEitherReturnMethod)
}

Use cases

The most common things I can think off where I use them avidly is complex asynchronous flows, such as OAuth1 or OAuth2, where I care about fine grained chaining of errors.

You can use \/ as the return of a Future:

def someComplexThirdPartyApiCall: Future[\/[Exception, CorrectReturn]] = {
}

Because you can flatMap over futures, you can chain a couple methods like the above, collect and propagate errors.

Example

  def method1: Future[\/[Exception, String]]
  def method2(result: String): Future[\/[Exception, String]]

  def chainExample: Future[\/[Exception, Int]] = {
    for {
      firstResult <- EitherT(method1)
      secondResult <- EitherT(method2(firstResult))
    } yield secondResult.toInt
  }
like image 164
flavian Avatar answered Oct 02 '22 15:10

flavian