链式和scala选项

时间:2014-10-03 14:37:31

标签: scala functional-programming

我已经configurer支持链样式,如下所示:

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

它的第三方库,我无法改变。

我有我的

  

case class Config(propA:Option [String],propB:Option [Int],propC:   选项[字符串])

现在我需要使用给定的configurer对象构建我的config,如果propX中设置了相应的值,则应调用方法config

以功能性方式做到这一点的最佳方法是什么?

我不喜欢这个

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)

觉得应该有一种优雅的方式。

3 个答案:

答案 0 :(得分:2)

由于您特别要求以功能方式执行此操作,因此我建议在每个选项上使用折叠,将Some转换为所需的函数,将None转换为identity:< / p>

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)

如果你真的喜欢冒险,你可以用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

在实际代码中,你可能想要使用Eugene的解决方案,因为你只是包装了一个不理想的API,重要的是从现在开始明确。

答案 1 :(得分:1)

你可以用var来做,通常它是scala中代码不好的标志,但在这种情况下我觉得它绝对可以接受。

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
}

答案 2 :(得分:1)

我会使用@ EugeneZhulenev的解决方案,但Option.fold而不是foreach保持不变(不需要转到@TravisBrown提出的高阶/ scalaz版本):

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
}