jcallin
jcallin

Reputation: 75

How to delay assignment of class values in Scala?

I have a class that gets created using data a read from a .conf file. The class also contains a value b which does not come from the conf file.

The class:

case class Configuration(a: String, b: String)

The sequence of calls to instantiate a Configuration looks like this:

User -> ConfigurationCompiler -> ConfigurationReader -> readConfig(): Configuration (The user gets a Configuration object back)

Value a gets read and set by the .conf file, but value b is specified by the user. I don't want to pass value b all the way down to readConfig() so it can be set on instantiation of Configuration.

I have experimented with Option, but to me it looks ugly because you first instantiate b with None, and then set it later. Also, the tests look weird because you must test Some(String) instead of String. Option also doesn't seem like it fits here because the field is actually not optional, it is just set at a later time.

The Option class:

case class Configuration(a: String, var b: Option[String])

Upvotes: 1

Views: 264

Answers (3)

bottaio
bottaio

Reputation: 5093

I would go for a builder. Here's an example:

import cats.implicits._
import cats.data.ValidatedNel

case class ConfigBuilder(a: Option[String] = None, b: Option[String] = None) {
  def withA(a: String): ConfigBuilder = copy(a = Some(a))
  def withB(b: String): ConfigBuilder = copy(b = Some(b))
  def build: ValidatedNel[String, Configuration] =
    (a.toValidNel("'a' must be defined"), b.toValidNel("'b' must be defined"))
      .mapN(Configuration)
}

Validating both fields:

ConfigurationBuilder()
  .build
// Invalid(NonEmptyList('a' must be defined, 'b' must be defined))

Getting valid configuration:

ConfigurationBuilder()
  .withA("test")
  .withB("test2")
  .build
// Valid(Configuration(test,test2))

Upvotes: 1

Bob Dalgleish
Bob Dalgleish

Reputation: 8227

You could create the original configuration object with a default value in the b position. When you get the actual value of b, then create a copy that is used by your program using:

val realConfig = originalConfig.copy( b = bValue )

The copy will have the field b replaced with the desired value for actual use.

Upvotes: 1

amorfis
amorfis

Reputation: 15770

Some solution might be to use implicit, but implicit String doesn't look good - implicit types are resolved by type, so it should be something less general than String.

What I can think of is something like PartialConfiguration returned by readConfig(). Then you can add it's values to the values provided by User to create full Configuration.

Upvotes: 1

Related Questions