0

I was wondering if there is any equivalent of the Scala fold ifEmpty function that exists for collections and options:

fold[B](ifEmpty: => B)(f: (A) => B)

This function is powerful in that it can transform between different monad types - for example the following transforms an Option[Person] to an Observable[Person]:

case class Person(name: String)

val observable = Option(Person("Paddy")).fold
  { Observable.just[Person]() }  // ifEmpty function
  { p => Observable.just(p) }    

My current problem is that I'd like something similar in the Reactive Extensions land. My actual scenario calls more for a "switch if empty" type of functionality - ideally:

// getFooFromCache returns Observable[Foo] from some cache service
// getFooFromDatabase returns Observable[Foo] from some database service

val sourceObservable = getFooFromCache()
sourceObservable.switchIfEmpty { getFooFromDatabase() }

So the idea here being if the source observable completes and emits nothing, then "switch" to another observable. A practical example is as suggested above - try and fetch something from a cache, if that returns nothing then fetch it from a database.

My current workaround to achieve the above looks like so:

getFooFromCache()
  .map { Option(_) }
  .orElse { None }
  .flatMap {
    case (Some(foo)) => Observable.just(foo)
    case _ => getFooFromDatabase()
  }

In other words - wrap the result in an Option (so that an empty result can then be emitted as a None value) and then flatMap with pattern matching.

EDIT: Here are a couple of extension functions that I will use if nothing exists:

object RxUtils {
  implicit class RxUtilExtensions[+T](val observable: Observable[T]) extends AnyVal {
    def toOption[U >: T]() : Observable[Option[U]] = {
      observable
        .map { Option(_) }
        .orElse { None }
    }
    def switchIfEmpty[U >: T](f: () => Observable[U]) : Observable[U] = {
      observable.toOption
        .flatMap {
          case(Some(t)) => Observable.just(t)
          case _ => f()
        }
    }
  }
}

With the above I can now do:

getFooFromCache("item.id")
  .switchIfEmpty { () => getFooFromDatabase("item.id") }
mixja
  • 6,977
  • 3
  • 32
  • 34

1 Answers1

1

If you are using RxScala 0.24 and are comfortable using "Experimental" APIs then you can

import rx.lang.scala.ExperimentalAPIs._

and just do

getFooFromCache("item.id").switchIfEmpty(getFooFromDatabase("item.id"))

Note that the parameter is eagerly evalutated, so to get the same effect as your workaround you will need to do

getFooFromCache("item.id").switchIfEmpty(Observable.defer(getFooFromDatabase("item.id")))
I think you want `orElse[U >: T](default: ⇒ U): Observable[U]` `getFooFromCache().orElse(getFooFromDatabase())` (This method is the [equivalent](http://reactivex.io/rxscala/comparison.html) of `defaultIfEmpty(T)` in the RxJava implementation on which RxScala is based.)
rxg
  • 3,777
  • 22
  • 42
  • orElse is a "map" style operation - in my case I want an orElse with a "flatMap" style operation. So I'm switching from one observable to another observable, not emitting a default item from a single observable if empty. – mixja Mar 18 '15 at 11:26
  • Sorry, I missed that bit. Since there is currently no access to `switchIfEmpty` in the underlying Java classes it looks like you will have to write your own as you suggest. – rxg Mar 18 '15 at 12:47