Blankman
Blankman

Reputation: 267140

How would you write a method that takes an optional inner function

I'm not sure what this pattern is called, but could this be written in scala and if so how would I go about doing this?

val userId = 1

def getUser(userId: Int): User = {
 CacheLookup.getOrElse(userId) {
   userDao.get(userId)
 }
}

I guess this would be an anonymous function as a parameter?

Upvotes: 0

Views: 91

Answers (2)

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297265

There's something similar to that on maps -- check mutable map's withDefault. The difference is that it doesn't update the map, which seems to be what you want (and, in fact, what many people using withDefault wants, by the questions we get here).

An alternative is memoization. You'd have to find that outside the standard library, but it works a bit like that except you don't do the cache thing explicitly. Basically, you write a function userId => userDao.get(userId), and then you get a memoized version of it. When you call the memoized version, it will check whether there's a cached version for the parameter and serve that if so.

Note that there are important issues with the cache. Do you want/can have it increase indefinitely, or should it be limited? Will the keys expire after a time? This sort of control is important, and whatever solution you pick must support it.

As for how to implement it, it could go like this:

def memoize[A, B](f: A => B): A => B = {
  var map = Map.empty[A, B]
  (key: A) => {
    if (!map.contains(key)) map += key -> f(key)
    map(key)
  }
}

val getUser = memoize((userId: Int) => userDao.get(userId))

This memoization function takes a Function1 (that is, a function with one parameter) and takes a Function1 that does caching (in a very basic sense, without any of the controls I mentioned). For functions taking more parameters you'd have to create different versions of this, or make them tupled.

Then we get to the use, where I pass the function that takes an userId (an Int) and returns a User, and we get the same function back, but now doing caching.

Here's an example of tupling, just out of curiosity:

scala> val mult = memoize(((x: Int, y: Int) => x * y).tupled)
mult: ((Int, Int)) => Int = <function1>

scala> mult(2, 3)
res18: Int = 6

scala> mult(2 -> 3)
res19: Int = 6

The first call, mult(2, 3) is actually a Scala oddity. When calling functions that take a tuple, if you are passing multiple parameters then Scala will auto-tuple then.

Upvotes: 2

Aaron Novstrup
Aaron Novstrup

Reputation: 21017

It's not entirely clear what you're asking. If you're wondering how to implement the getOrElse method so that the database call would only be conditionally evaluated, the answer is to just use an ordinary higher-order function:

def getOrElse(userId: Int)(getter: Int => User) =
  if (cache contains userId) 
    cache get userId
  else
    getter(userId)

Upvotes: 1

Related Questions