eranga eranga - 3 months ago 8
Scala Question

Functional way to get or update

I have a cache and have two functions to get an item from the cache and put an item to the cache.

When getting item(from the cache), if key not exists I need to populate a value to it and return that value.

Following is a sample code

class CacheComp {
cache = Map[String, Foo]

get(id): Foo = {
// case(id exists) => Return matching Foo

// case(id not exists) => Create a Foo and update the cache with created Foo. Then return updated Foo
}

put(id, Foo) = {
// put element to the cache
}
}


In here I'm violating single responsibility principle(SRP) of
get(id)
function. How to do this without violating SRP? I can simply rename the function to
getOrUpdate(id)
. But are there any clean functional programming way to do that?

Tim Tim
Answer

If you're aiming for a 'functional' solution, you want your cache Map to be immutable, because everything is immutable in the functional world. Note that scala.collection.immutable.Map has this method:

override def updated [B1 >: B](key: A, value: B1): Map[A, B1]

Now there is a small wrinkle -- once the map is updated, how do you use the cache with updated value? You need to change your interface for this.

type Cache = Map[String, Foo]

object Cache {
  def get(id: String, cache: Cache): (Foo, Cache) = cache.get(id) match {
    case Some(e) => (e,cache)
    case None => 
      val foo = makeFoo
      (foo, cache.updated(id, foo))
  }

  def put(id: String, foo: Foo, cache: Cache): Cache = cache.updated(id, foo)
}

That gives you a functional cache without side effects. I'd also further change put to upsert and check if a cache entry needs to be updated.