ktonga
ktonga

Reputation: 190

Implicit vars and Futures

I have an issue using an implicit var and futures. Suppose the following scenario:

object ImplicitMess extends App {

  implicit var curStr = "Initial Value"

  def useImplicit(implicit str: String) = {
    println(str)
    str.length
  }

  useImplicit

  val ftr = future {
    Thread.sleep(1000)
    useImplicit
  }

  curStr = "Modified Value"

  Await.ready(ftr, 2.seconds)

}

For the time the future was created the implicit value was "Initial Value" but when it's actually executed the value is "Modified Value", wich is not the desired behavior. It's that way because the future body is referencing the var and not the current value.

So the first solution I came up with was to capture the var value in a val, within a block, thinking that this will scope the implicit closer and solve the ambiguity. But it doesn't even compile due to "ambiguous implicit values" error.

...
  val ftr = {
    implicit val str = curStr
    future {
      Thread.sleep(1000)
      useImplicit
    }
  }
...

So, what I would love to have, is some way to wrap async code (any operation on futures) where you work with a fixed implicit value at the time the async code was defined, not when it's evaluated.

It is for a library, the examples are a really simplified version of the problem, so I need something like this for two reasons: 1) For achieving the desired behavior. 2) Take off from user the responsibility to know about all that complexity.

...
  val ftr = fixImplicit {
    // All within this block will use the current value of the implicit var
    future {
      Thread.sleep(1000)
      useImplicit
    }
  }
...

Do you think is that possible?
Thank you.

[EDITED] Adding some context for all of you that are horrified about the var usage.

I need this to improve one of my projects, which is akka-contextual-actor (github), it is all about propagating a common context between actors avoiding the use of an aspect library. So one of the main needs is to do it transparently, without passing the implicit explicitly.

I do it wrapping and unwrapping messages relying a lot on implicit conversions and I don't have another option than having the context in a var in the actor for making it available in the receive. You can take a look at the code for seeing how I do it.

Thanks again.

Gaston.
@ktonga

Upvotes: 0

Views: 475

Answers (2)

som-snytt
som-snytt

Reputation: 39577

The other answer is that you must shadow the implicit.

scala> :pa
// Entering paste mode (ctrl-D to finish)

object ImplicitMess extends App {

  implicit var curStr = "Initial Value"

  def useImplicit(implicit str: String) = {
    println(str)
    str.length
  }

  useImplicit

  val ftr = {
    implicit val curStr = ImplicitMess.curStr
    future {
    Thread.sleep(1000)
    useImplicit
  }}
  curStr = "Modified Value"

  Await.ready(ftr, 2.seconds)

}

// Exiting paste mode, now interpreting.

warning: there were 1 deprecation warning(s); re-run with -deprecation for details
defined object ImplicitMess

scala> ImplicitMess main null
Initial Value
Initial Value

Of course, the advice about vals still applies.

Update:

If we're talking about this context then I think this answer addresses it.

Upvotes: 2

Gangstead
Gangstead

Reputation: 4182

Firstly are you sure you need to use vars? Vals are more idiomatic.

If you need to access the value of the var before the future completes you can just save a copy and then explicitly use that param:

  val ftr = future {
    val saved = curStr
    Thread.sleep(1000)
    useImplicit(saved)
  }

Upvotes: 2

Related Questions