What does "ap" of \/ in Scalaz do?
Asked Answered
M

1

7

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 ?

Mensa answered 18/3, 2014 at 15:8 Comment(0)
M
4

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
  }
Mumbletypeg answered 18/3, 2014 at 15:28 Comment(8)
Thank you ! Could explain please how ap behaves just like EitherT ? Why should one use EitherT in the first place ?Mensa
Thank you for the update. I am starting getting it ... Suppose I have f1: A => Future[\/[Exception, B] and f2: B => Future[\/[Exception, C]] how exactly can I compose them with \/.ap to get f3:A => Future[\/[Exception, C]?Mensa
@Mensa You have map and flatMap.Mumbletypeg
Could you give an example of composing f1 and f2 to create f3 (as those functions defined in the comment above) ?Mensa
@Mensa Updated with example.Mumbletypeg
Thank. Could you explain where exactly ap is used in this example ?Mensa
@Mensa It's not directly visible, however it's the thing that says do the action only if the result "is a right".Mumbletypeg
Thanks. I will think it over again.Mensa

© 2022 - 2024 — McMap. All rights reserved.