Search code examples
scalaconcurrency

how to construct Scala API with error sentinel token (like Option.None) that can be chained through flatmap?


I recently tried a variation on some of the concurrency exercises in Functional Programming in Scala (great book, btw.. and early access edition is downloadable free!). Chapter 7 involves creating a monadic type constructor Par[X], which enables any expression of type X to be forked off and run in parallel when paired with an ExectutorService

My attempted variation was to create an API that allowed callers to flatmap parallel expressions (instances of the parameterized type Par[X]) together. I wanted something with a similar flavor to Option/Some/None wherein if one of the flatmap results in the chain happens to be a failure (a 'None' in the case of Option), then that failure that 'bubbles up' to the top, so that the caller that constructed the chain only needs to check that the top level return value is not the error sentinel. (Hope what I want to do is clear from this explanation). Anyway... I ran into a problem constructing the error sentinel.

I tried to contruct it two ways, as a case object, and as a case class. In the first instance, the case object, I got this error

    Object creation impossible since member get():V in java.util.concurrent.future is not defined.

Here is my code for the first case:

    case object Failure extends Future[Nothing] {
    def isDone = true

    // why is compiler telling me i'm not defining this, when def'n is below?
    def get(timeout: Long, units: TimeUnit) =  
        throw new NoSuchElementException("nothing to get from failure")

    def isCancelled = false

    def cancel(evenIfRunning: Boolean): Boolean = false
    }

I then tried using case class which extended from Future[Nothing], like so:

  case class Failure2 extends Future[Nothing] {
    def isDone = true

    def get(timeout: Long, units: TimeUnit) =  throw new NoSuchElementException("nothing to get from failure")

    def isCancelled = false

    def cancel(evenIfRunning: Boolean): Boolean = false
  }

This resulted in the following error:

class Failure2 must be declared abstract or implement abstract member get():V in java.util.concurrent.future 

If any of you expert Scala API builders can help guide my to a solution I would be very grateful !


Solution

  • When the error messages refer to

    member get():V in java.util.concurrent.Future

    they're referring to the get() method of Future with no arguments, which you indeed forgot to implement. I'm guessing you wanted both versions of get() to do the same thing. You just have to be explicit about that. For example:

    def get() = throw ...
    def get(timeout: Long, units: TimeUnit) = get()