Search code examples
scalascala-cats

how to return either value or throw error in Scala


I have a list of emails, for each one I'll look it up in email table to see if that email exist. if it does, do nothing else I'll throw error. here is my code;

def lookupEmailStatus(email: EmailAddress, requestId: RequestId)(
      implicit ec: ExecutionContext): HttpServiceResult[List[EmailStatusDTO]] = {
    emailDatabase
      .getEmailStatusByEmail(email, requestId)
      .map(
        l =>
        if (l.isEmpty) {
          logger.error(
            LoggingMessage(
              requestId,
              s"Email status not found by ${email.email} failed"))
          EntityNotFound(s"${email.email}", requestId)
        } else {
          l
        }
      )
      .leftMap[HttpError] {
        case e =>
          logger.error(
            LoggingMessage(
              requestId,
              s"Retrieve email status by ${email.email} failed"))
          DatabaseError(e.message, requestId)
      }
  }

when I ran the code got error:

Error:(57, 27) type mismatch;
 found   : cats.data.EitherT[model.HttpError,Product with Serializable]
 required: model.HttpServiceResult[List[EmailStatusDTO]]
    (which expands to)  cats.data.EitherT[model.HttpError,List[EmailStatusDTO]]
      .leftMap[HttpError] {

if I removed the .map(..) method it'll works fine but that's not what I want:

def lookupEmailStatus(email: EmailAddress, requestId: RequestId)(
          implicit ec: ExecutionContext): HttpServiceResult[List[EmailStatusDTO]] = {
        emailDatabase
          .getEmailStatusByEmail(email, requestId)
          .leftMap[HttpError] {
            case e =>
              logger.error(
                LoggingMessage(
                  requestId,
                  s"Retrieve email status by ${email.email} failed"))
              DatabaseError(e.message, requestId)
          }
      }

here are the type definitions:

type HttpServiceResult[A] = ServiceResult[HttpError, A]
type ServiceResult[Err, A] = EitherT[Future, Err, A]

Solution

  • As @dmytro-mitin already mentioned, the root cause of your issue is that you're not returning the same type in both branches of your conditional. One way to fix that would be to make sure that you're returning the correct type.

    A different and, in my opinion, better way would be to use cats.data.EitherT.ensure for the list.isEmpty check. That way you're being explicit about what you care about (i.e. if the list is empty, return an error) and don't have to manually deal with the happy case.

    Your code would then become:

    def lookupEmailStatus(email: EmailAddress, requestId: RequestId)(
          implicit ec: ExecutionContext): HttpServiceResult[List[EmailStatusDTO]] = {
        emailDatabase
          .getEmailStatusByEmail(email, requestId)
          .ensure({
             logger.error(LoggingMessage(requestId, s"Email status not found by ${email.email} failed"))}
             EntityNotFound(s"${email.email}", requestId)
          })(!_.isEmpty)
          .leftMap[HttpError] {
            case e =>
              logger.error(
                LoggingMessage(
                  requestId,
                  s"Retrieve email status by ${email.email} failed"))
              DatabaseError(e.message, requestId)
          }
      }