Chain style + scala

I have configurer

one that supports chain style like this:

val configurer = Configurer("init").propA("a").propB(3).propC("bla-bla")

      

it is a third party library which I cannot change.

And i have

case class Config (propA: Option [String], propB: Option [Int], propC: Option [String])

Now I need to build my configurer

with a given object config

, the method propX

should be called if the corresponding value is set to config

.

What's the best way to do this in a functional way?

I do not like this

val configurer = Configurer("init")
val withPropA = config.propA.map(configurer.propA).getOrElse(configure)
val withPropB = config.propB.map(configurer.propB).getOrElse(withPropA)
val withPropC = config.propC.map(configurer.propC).getOrElse(withPropB)

      

Just feel like there must be an elegant way.

+3


source to share


3 answers


You can do this with var, which is usually a sign of bad code in scala, but in this case I find it perfectly acceptable.



def buildConfigurer(propA: Option[String], propB: Option[Int], propC: Option[String]) = {
   var configurer = new Configurer("init")
   propA.foreach(a => configurer = configurer.propA(a))
   propB.foreach(b => configurer = configurer.propB(b))
   propC.foreach(c => configurer = configurer.propC(c))
   configurer
}

      

+1


source


Since you specifically asked about this in a functional way, I would suggest using a fold on each option that converts Some

to the function you want and None

to identity

:

config.propA.fold(identity[Configurer] _)(a => _ propA a) andThen
config.propB.fold(identity[Configurer] _)(b => _ propB b) andThen
config.propC.fold(identity[Configurer] _)(c => _ propC c)

      

If you're really adventurous, you can make it a little more elegant with Scalaz:



import scalaz._, Scalaz._

config.propA.map(a => Endo[Configurer](_ propA a)).orZero |+|
config.propB.map(b => Endo[Configurer](_ propB b)).orZero |+|
config.propC.map(c => Endo[Configurer](_ propC c)).orZero

      

In real code, you probably want to use Eugene's solution, though, since you're just wrapping an API that's not perfect and it's important that it's clear here.

0


source


I would use something like @EugeneZhulenev's solution, but Option.fold

instead foreach

stayed the same (without going over to the higher order / scalar version suggested by @TravisBrown):

def buildConfigurer(cfg: Config): Configurer = {
  val with0 = new Configurer("init")
  val withA = cfg.propA.fold(with0)(with0.propA(_))
  val withB = cfg.propB.fold(withA)(withA.propB(_))
  val withC = cfg.propC.fold(withB)(withB.propC(_))
  withC
}

      

0


source







All Articles