Failure not related to Future?

71 views Asked by At

I’m learning Scala with the official course on Coursera. I’m speaking Java quite fluent, but this is something new. I get a knot in my brain.

The last programming assesment deals with asynchronous programming. I struggle with implementing a method of the companion object:

case class WikiResult[A](value: Future[Either[Seq[WikiError], A]])
object WikiResult:
  def successful[A](a: A): WikiResult[A] = WikiResult(Future.successful(Right[Seq[WikiError], A](a)))

  def traverse[A, B](as: Seq[A])(f: A => WikiResult[B])(using ExecutionContext): WikiResult[Seq[B]] =
    as match
      case Seq() => WikiResult.successful(Seq.empty)
      case _ => as.foldLeft(WikiResult.successful(Seq.empty))((wili, a) => {
        val b = f(a)
        b.value match
          case Failure(_) => WikiResult.successful(Seq.empty)
          case _ => WikiResult.successful(Seq.empty)
      })

This of course is no complete solution but a try to get the syntax right.

If the given as is empty, i return a new WikiResult with an empty value sequence.

Else, i iterate over as (as hinted in the task) with foldleft(). For each value a in as, i call the given function f. b.value can have three different states: Success(Right(val: B)) if everything worked, Success(Left(errlist: Seq[WikiError])) if a domain error happened, or Failure(exc: WikiException) if a system failure occured.

ATM, i’m testing only for the last case. To avoid incompleteness, i add the generic case. In both cases, i simply return an empty result.

To my big surprise, i get this case is unreachable since type concurrent.Future[Either[Seq[wikigraph.errors.WikiError], B]] and class Failure are unrelated. Whereever i read about scala´s Future, i found that matches are done with Success and Failure. I cannot imagine what i did wrong?

IDE is IntelliJ IDEA Community Edition. Scala and JVM are provided by IDE.

I found a lot of "case unreachable" questions but none correlated to my problem.

Any hints?

TIA QNo

1

There are 1 answers

0
Dmytro Mitin On

There are methods Future.successful(...) and Future.failed(...).

But it's Try that is an ADT with two cases Success and Failure, not Future

sealed abstract class Try[+T] extends Product with Serializable
final case class Success[+T](value: T) extends Try[T]
final case class Failure[+T](exception: Throwable) extends Try[T]

There is .value that transforms a future into Option[Try[T]]. The thing is that at current moment a future is not necessarily successful or failed, maybe it's not completed yet.

If you want "to get T from Future[T]" then you can block with Await.result or register a callback with future.onComplete(...). Or you can keep working with Futures. By the way, there is Future.traverse.

Using Cats,

import cats.data.Nested
import cats.syntax.traverse._
import scala.concurrent.{ExecutionContext, Future}

trait WikiError

case class WikiResult[A](value: Future[Either[Seq[WikiError], A]])
object WikiResult:
  def successful[A](a: A): WikiResult[A] = WikiResult(Future.successful(Right[Seq[WikiError], A](a)))

  def traverse[A, B](as: Seq[A])(f: A => WikiResult[B])(using ExecutionContext): WikiResult[Seq[B]] =
    WikiResult(as.traverse(a => Nested(f(a).value)).value)

https://scastie.scala-lang.org/DmytroMitin/c798BQMPT4iQHLEB8u30QQ/6