Question

I have the following snippet of code:

val oldStep = step
try {
  someDangerousActionPotentiallyModifyingStep()
} finally {
  step = oldStep
}

Is it possible to write a generic helper method to abstract away the pattern? Then I could use the helper method like this:

remember(step) {
  someDangerousActionPotentiallyModifyingStep()
}

Of course the following approach does not work, because Scala does not support pass by reference:

def remember[T](x: T)(action: => Unit) {
  val previousValue = x
  try {
    action
  } finally {
    x = previousValue
  }
}

I solved the problem by changing the step data member into an implicit parameter. That way, I never have to restore the old value of step, because its value never changes inside the same scope :)

Was it helpful?

Solution

The problem is that a var does not give you enough control to do what you want to do.

As a direct answer to your question: you need an object with an update method instead of a field. Something like this:

class Box[T](var value:T) { 
  def apply = value
  def update(newValue:T) { value = newValue }
  override def toString = value.toString 
} 

Then you can solve your problem like this:

def remember[T](box:Box[T])(action: =>Unit) { 
  val prev = box.apply
  try { 
    action 
  } catch { 
    case _ => box() = prev 
  } 
}

val step = new Box(0)

// working update
remember(step) { step() = 4 }

// step is now 4

// aborted update
remember(step) { step() = 5; throw new Exception }

// step is still 4

But note that this is really not very idiomatic scala. You should try to make your someDangerousActionPotentiallyModifyingStep a side-effect-free function that returns the new step.

Basically like this:

try {    
  step = someDangerousFunctionOfStep(step)
} catch {
  case _ => // we don't have to do anything because step is still the same
}

If that is not possible for some reason, you might want to investigate Akka agents, which are conceptually similar to the Box above, except with the difference that they are thread-safe and can be used in a transactional way.

Here is how you would use akka agents to solve the problem:

First you need an actor system:

implicit val actorSystem = akka.actor.ActorSystem("test")

Then you can define an agent containing the step value

val step = akka.agent.Agent(0)

Now you can update it in a transaction:

import scala.concurrent.stm._    

atomic { txn => step() = 4 } 
// step.get will now return 4

atomic { txn => step() = 5; throw new Exception }
// step.get will still return 5. You will have to catch the exception if you don't want
// it to propagate outward

The real power of akka agents comes when you have multiple agents and update them atomically. See the akka agents docs for the canonical "account transfer" transaction example.

OTHER TIPS

It's pretty ugly (and an affront to immutability), but you could try something like this:

def remember[T](newT:T, get: => T, set:(T) => Unit)(action: => Unit) = {
  var old = get
  set(newT)
  try{
    action
  }
  finally{
    set(old)
  }
}

And then use it like this:

var x = "hello"

remember[String]("world", x, x = _){
  //do something dangerous here 
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top