Reputation: 304
Is there a way to instantiate a top level lazy value based on value we get in a method
trait Props {
val env: String
}
object Exc extends App {
Foo.getInput("dev")
}
object Foo extends Props {
lazy val env: String = ???
def getInput(e: String): Unit = {
//How do I instantiate env with e here
env = e //this doesn't work
}
def exampleMethod() = println(env)
}
object Foo has to extend trait Props, so I need to figure out a way how to instantiate the variable env , based on what's passed in the method
Upvotes: 0
Views: 124
Reputation: 20591
The closest thing to what you want may well be a Future
, which is the other notable case in the language and standard library where evaluation can be delayed and a result be write-once.
So something like the following
import scala.concurrent.{ Await, Future, Promise }
import scala.concurrent.duration.Duration
object Foo extends Props {
private val envPromise = Promise[String]()
lazy val env: String = Await.result(envPromise.future, Duration.Inf)
def getInput(e: String): Unit = {
envPromise.success(e)
}
}
If env
is accessed before getInput
is called, it will block the accessing thread for arbitrarily long until some other thread calls getInput
(if no other thread does, it's effectively an infinite loop). Alternatively one can put a finite duration (e.g. 3.minutes
) on the Await
, which will throw an exception if getInput
hasn't been called in that time. The exception in the lazy
initializer will propagate to the accessing thread (which may be a bit of a surprise: someone given a Props
would reasonably expect that accessing the env
(a val
) would not blow up), but a subsequent access after getInput
was called will have the value.
Multiple calls to getInput
will throw exceptions because a Promise
can be completed at most one time.
My honest opinion is that it may be worth revisiting the decisions which led up to this. I would specifically consider some alternative like:
object Foo {
private val propsPromise = Promise[Props]()
val propsFuture: Future[Props] = propsPromise.future
def getInput(e: String): Unit =
propsPromise.success(new Props { val env: String = e })
}
Where you are delaying construction of a valid Props
instance but can statically provide a reference to that instance for if/when it exists.
Upvotes: 2
Reputation: 293
In this case env
should be var
:
var env: String = ???
lazy
here is not needed
Upvotes: 0