Freewind
Freewind

Reputation: 198248

How to create a method which invokes another service and return a Future?

I want to define a method, which will return a Future. And in this method, it will call another service which returns also a Future.

We have defined a BusinessResult to represent Success and Fail:

object validation {
  trait BusinessResult[+V] {
    def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T]
    def map[T](f: V => T): BusinessResult[T]
  }

  sealed case class Success[V](t:V) extends BusinessResult[V] {
    def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T] = {
      f(t)
    }
    def map[T](f: V => T): BusinessResult[T] = {
      Success(f(t))
    }
  }

  sealed case class Fail(e:String) extends BusinessResult[Nothing] {
    def flatMap[T](f: Nothing => BusinessResult[T]):BusinessResult[T] = this
    def map[T](f: Nothing => T): BusinessResult[T] = this
  }

}

And define the method:

import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import validation._

def name: BusinessResult[String] = Success("my name")

def externalService(name:String):Future[String] = future(name)

def myservice:Future[Int] = {
  for {
    n <- name
    res <- externalService(n)
  } yield res match {
    case "ok" => 1
    case _ => 0
  }
}

But which is not compilable. The code in myservice can't return a Future[Int] type.

I also tried to wrap the name with Future:

def myservice:Future[Int] = {
  for {
    nn <- Future.successful(name)
    n <- nn
    res <- externalService(n)
  } yield res match {
    case "ok" => 1
    case _ => 0
  }
}

Which is also not compilable.

I know there must be a lot of issues in this code. How can I adjust them to make it compilable?

Upvotes: 0

Views: 101

Answers (1)

Ende Neu
Ende Neu

Reputation: 15783

If you change the n with some hardcoded string it works, the problem is that in the for comprehension the variable n has type BusinessResult[String], as you probably already know for comprehension desugarize to map, flatMap and filter so the first part n <- name desugarize to a map on name:

val test: BusinessResult[String] = name.map(x => x)

Intellij thinks n is a String but the scala compiler disagree:

type mismatch;
[error]  found   : validation.BusinessResult[Nothing]
[error]  required: scala.concurrent.Future[Int]
[error]       n <- name
[error]         ^

Easy solution could be to add a getter method to get back the string and do something like Option does:

object validation {
  trait BusinessResult[+V] {
    def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T]
    def map[T](f: V => T): BusinessResult[T]
    def getVal: V
  }

  sealed case class Success[V](t:V) extends BusinessResult[V] {
    def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T] = {
      f(t)
    }
    def map[T](f: V => T): BusinessResult[T] = {
      Success(f(t))
    }
    def getVal: V = t
  }

  sealed case class Fail(e:String) extends BusinessResult[Nothing] {
    def flatMap[T](f: Nothing => BusinessResult[T]):BusinessResult[T] = this
    def map[T](f: Nothing => T): BusinessResult[T] = this
    def getVal = throw new Exception("some message")
  }
}

def myservice: Future[Int] = {
  val value = name.getVal
  for {
    res <- externalService(value)
  } yield res match {
    case "ok" => 1
    case _ => 0
  }
}

Note that you can't extract the name in the for comprehension since map on String return Chars

Upvotes: 1

Related Questions