ni_i_ru_sama
ni_i_ru_sama

Reputation: 304

Instantiate a top level lazy value based on value we get in a method scala

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

Answers (2)

Levi Ramsey
Levi Ramsey

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

Bondarenko
Bondarenko

Reputation: 293

In this case env should be var:

var env: String = ???

lazy here is not needed

Upvotes: 0

Related Questions