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.
source to share
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
}
source to share
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.
source to share
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
}
source to share