1

Context

For the sake of being able to change the config for testing, I have a trait with almost all the code. Then in a case class I am trying to pass in the config. In my testing I have a testing case class.

This works (but is not the funcionality I want)

trait Greeting(val name: String) {
  def msg = s"How are you, $name"
}

class prodEnvClass extends Greeting("Production Env") {
  println(msg)
}

class testEnvClass extends Greeting("Testing Env") {
  println(msg)
}

This does not work (but is the functionality I want)

trait Greeting(val name: String) {
  def msg = s"How are you, $name"
}

class prodEnvClass extends Greeting(myEnv) {
  val myEnv = fetchProdEnv()
  println(msg)
}

class testEnvClass extends Greeting(myEnv) {
  val myEnv = fetchTestEnv()
  println(msg)
}

Question

How do I pass a parameter made inside the case class body into the trait? The code that works does not match my use case. I want to use code inside the case class to make the input to the trait.

Rorschach
  • 3,684
  • 7
  • 33
  • 77
  • 1
    You can't AFAIK. (I also don't see a `case class` in your code btw). How would you initialize this? You need to initialize the trait to initialize the class but you also need to initialize the class to initialize the trait ? There is a conflict right there. You should make the class final and have a companion object initialize the class as needed. It can load the required config and pass it along. You can use [ciris](https://cir.is/) to load in the corresponding environments – sinanspd Dec 15 '20 at 05:37
  • Although I should note, you could have the method take the name as a parameter. That is the simple solution but that's not how I would design this – sinanspd Dec 15 '20 at 05:43
  • There is a similar post that needs the same functionality for a different purpose: [Extend Java abstract class in Scala](https://stackoverflow.com/q/65276291/2359227) – Tomer Shetah Dec 15 '20 at 07:12

1 Answers1

6

You seem to be mixing function invocation with inheritance in a way that is not supposed to work.

For function invocation, you

  • create binders as a function parameters, and then
  • pass values as arguments,

something like this:

def f(s: String) { println(s) }
f("hello (function invocation)")

When using inheritance, you

  • create binders by naming abstract members, and later you
  • pass values by providing implementations for those abstract members

something like this:

trait C { def s: String; def m() = println(s) }
new C { val s = "hello (inheritance)"; m() }

In your code, you seem to attempt to

  • bind the value as constructor parameter (as you would do for functions)
  • pass the value myEnv by overriding it differently in two subclasses (as you would do for inheritance)

Just pick one mechanism. If you want inheritance, then do inheritance:

class MyEnv(val name: String)

trait Greeting {
  def myEnv: MyEnv
  def msg = s"hello ${myEnv.name}"
}

def fetchProdEnv() = new MyEnv("prod")
def fetchTestEnv() = new MyEnv("test")

class ProdEnvClass extends Greeting {
  val myEnv = fetchProdEnv()
  println(msg)
}

class TestEnvClass extends Greeting {
  val myEnv = fetchTestEnv()
  println(msg)
}

new ProdEnvClass() // "hello prod"
new TestEnvClass() // "hello test"

but don't try to use two halves (one half from each mechanism), they don't mix this way.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93