0

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

ktonga
  • 190
  • 7
  • 7
    I'd never realized implicit vars were even possible, and can't think of anything much more horrifying in Scala. Are you absolutely sure you have to do this? – Travis Brown May 08 '14 at 20:52
  • var capture http://scalapuzzlers.com/#pzzlr-008 – som-snytt May 08 '14 at 23:32
  • @TravisBrown your implicit execution context should totally be a var, so you can switch it out whenever! – som-snytt May 08 '14 at 23:33
  • shadowing implicits http://scalapuzzlers.com/#pzzlr-033 – som-snytt May 08 '14 at 23:46
  • 1
    Hi @TravisBrown, you made me laugh out loud with the "anything much more horrifying". I was really sure that the first answer or comment would be to warn me about using vars. I know it's awful. I've edited the question for adding some more context. – ktonga May 09 '14 at 02:33

2 Answers2

2

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)
  }
Gangstead
  • 4,152
  • 22
  • 35
  • Thank you @Gangstead, I was aware of this solution, but sadly, passing the value explicitly is not an option. I edited the question for explaining why. – ktonga May 09 '14 at 02:36
2

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.

Community
  • 1
  • 1
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • Thank you @som-snytt, it seems to be a valid solution, works as expected. It would be better not to put this responsibility on lib's user side (question edited), but it is acceptable so far. – ktonga May 09 '14 at 02:51