Skip to content

Instantly share code, notes, and snippets.

@matfournier
Last active September 18, 2019 06:45
Show Gist options
  • Save matfournier/33dea2b3ed347b253f3737ce3cc50738 to your computer and use it in GitHub Desktop.
Save matfournier/33dea2b3ed347b253f3737ce3cc50738 to your computer and use it in GitHub Desktop.
traversing all the things examples
package mf.examples
import scala.concurrent.Future
import scala.concurrent.ExecutionContext
import scala.util.Try
import cats._
import cats.data._
import cats.implicits._
case class Request(value: String)
case class RawResponse(value: String)
case class ParsedResponse(value: Int)
case object ParsedResponse {
def fromString(s: String): Either[ServiceError, ParsedResponse] =
Try(ParsedResponse(s.toInt)).toEither.leftMap(_ => ServiceError("parsing error"))
}
case class BatchRequest(requests: List[Request])
case class BatchResponse(responses: List[RawResponse])
case class ServiceError(message: String)
object Http {
def requestBatch(requests: List[Request], batchSize: Int = 2)(implicit ec: ExecutionContext): Future[Either[ServiceError, List[ParsedResponse]]] = {
val batches = requests.grouped(batchSize).map(batch => BatchRequest(batch)).toList
// this is another alternative and will fail if either the network fails OR the ANY parsing fails
val batchResponseE = Future.sequence(batches.map(batch => {
for {
response <- doRequest(batch)
} yield response.flatMap(br => parseAndCollectBatchResponse(br))
})).map(_.combineAll)
// this is another alternative if network failure means fail the entire thing
// but parsing failure is ok
val batchResponseParseFailureOk = Future.sequence(batches.map(batch => {
for {
response <- doRequest(batch)
} yield response.map(br => oParseAndCollectBatchResponse(br))
})).map(_.combineAll)
// what if this was EitherT, and ANY failure meant failure
val batchesTAnyFailure = batches
.traverse(br => {
val inner = EitherT(doRequest(br)).flatMap(br => EitherT.fromEither[Future](parseAndCollectBatchResponse(br)))
inner
}).map(_.combineAll)
// what is this was EitherT and parsing failures were ok but not network failures
val batchesOT = batches
.traverse(br =>
EitherT(doRequest(br)).map(br => oParseAndCollectBatchResponse(br))
).map(_.combineAll)
// tricky variation: fail on first network failure but accumulate _all parsing errors_
val batchResponsesNel = Future.sequence(batches.map(batch => {
for {
response <- doRequest(batch)
} yield response.toValidatedNel.map(parseAndCollectBatchResponseAllErrors).combineAll
})
).map(_.combineAll)
// tricky variation: accumulate _all_ failures
// can make this legible in many ways, this was just a lazy first cut w/o changing anything else.
val batchResponsesNelFailFuture = {
val networkFailuresCombined = Future
.sequence(batches.map(batch =>
doRequest(batch).map(_.toValidatedNel).map(_.map(parseAndCollectBatchResponseAllErrors(_)))
)
)
networkFailuresCombined.map(_.map(_.combineAll))
}
// and as an eitherT: trick question, there is no monad transformer for Validation.
batchesTAnyFailure.value
}
// if one things fails to parse error w/ it's error, otherwise give all the responses
private def parseAndCollectBatchResponse(br: BatchResponse): Either[ServiceError, List[ParsedResponse]] =
br.responses.traverse(r => ParsedResponse.fromString(r.value))
private def parseAndCollectBatchResponseAllErrors(br: BatchResponse): ValidatedNel[ServiceError, List[ParsedResponse]] =
br.responses.traverse(r => ParsedResponse.fromString(r.value).toValidatedNel)
// ignore any parsing failures
private def oParseAndCollectBatchResponse(br: BatchResponse): List[ParsedResponse] =
br.responses.flatMap(r => ParsedResponse.fromString(r.value).toOption)
private def doRequest(request: BatchRequest): Future[Either[ServiceError, BatchResponse]] = {
if (request.requests.length > 5) Future.failed(new Exception("http error"))
else {
val rawResponses = request.requests.map(r => RawResponse(r.value))
Future.successful(BatchResponse(rawResponses).asRight[ServiceError])
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment