【问题标题】:Apply all filter functions to a value将所有过滤器函数应用于一个值
【发布时间】:2017-07-03 13:56:43
【问题描述】:

我有一个看起来像这样的函数:

def createBuilder(builder: InitialBuilder, name: Option[String], useCache: Boolean, timeout: Option[Long]): Builder = {
    val filters: List[Builder => Option[Builder]] = List(
      b => name.map(b.withName),
      b => if (useCache) Some(b.withCache) else None,
      b => timeout.map(b.withTimeout))

    filters.foldLeft(builder)((b,filter) => filter(b).getOrElse(b))
}

它定义了来自Builder => Option[Builder]的3个过滤函数(从可选参数转换而来)。我想将它们应用到现有的builder 值,所以在None 的情况下,我可以原封不动地返回自己。

上面的代码是我能想到的最好的代码,但我觉得我应该能够以某种方式使用 Monoid 来做到这一点——如果是 None,则返回 identity

不幸的是,我不知道如何定义一个有意义的。或者,是否有更好/不同的方法?

如果这很重要,我正在使用 Cats。有什么想法吗?

【问题讨论】:

  • 在无的情况下返回标识。返回幺半群的标识(零)?这意味着如果你有一个Monoid[String].zero,你会返回一个空字符串,可以吗?
  • @YuvalItzchakov 嗯,我确定我的意思是应用 identity 函数来返回初始的 builder...
  • 啊,你说的是Monoid[Builder]
  • @YuvalItzchakov 不知道!这就是我在这里的原因,我猜:D
  • 感觉不像A => M[A]。将filters 映射到内胚函数列表val fs: List[Builder => Builder] = filters.map(f => (b: Builder) => f(b).getOrElse(b))foldK 这个List 似乎更自然。 (我认为 foldK 应该与 -Ypartial-unification 一起自动工作,但我无法使其工作,因此您可能必须手动提供类型参数。此外,猫会反向折叠列表,因为它使用 compose 代替andThenMonoidKcombineK)

标签: scala functional-programming scalaz scala-cats monoids


【解决方案1】:

我认为在你的情况下A => M[A] 结构有点多余。您在示例中使用的过滤器函数实际上等价于Option[Builder => Builder]。那是因为您不使用他们的Builder 参数来决定结果应该是Some 还是None。并且您可以进一步将函数简化为Builder => Builder.getOrElse(identity)

这里有 2 个使用这个想法的实现。他们甚至不真正依赖猫。

def createBuilder(
  builder: InitialBuilder, name: Option[String], useCache: Boolean, timeout: Option[Long]
): Builder = {
  def builderStage[T](param: Option[T])(modify: T => Builder => Builder): Builder => Builder =
    param.fold(identity[Builder](_))(modify)

  val stages: List[Builder => Builder] = List(
    builderStage(name)(n => _ withName n),
    // `Boolean` is equivalent to `Option[Unit]`, and we convert it to that representation
    // Haskell has a special function to do such a conversion `guard`.
    // In Scalaz you can use an extension method `useCache.option(())`.
    // In cats a similar `option` is provided in Mouse library.
    // But you can just write this manually or define your own extension
    builderStage(if (useCache) ().some else none)(_ => _.withCache),
    builderStage(timeout)(t => _ withTimeout t)
  )

  // It should be possible to use `foldK` method in cats, to do a similar thing.
  // The problems are that it may be more esoteric and harder to understand, 
  // it seems you have to provide type arguments even with -Ypartial-unification,
  // it folds starting from the last function, because it's based on `compose`.
  // Anyway, `reduceLeft(_ andThen _)` works fine for a list of plain functions. 
  stages.reduceLeft(_ andThen _)(builder)
}

另一种可能性是flattenOptions 的List,它只是删除Nones 而不会强制它们为identity

def createBuilder2(
  builder: InitialBuilder, name: Option[String], useCache: Boolean, timeout: Option[Long]
): Builder = {
  val stages: List[Option[Builder => Builder]] = List(
    name.map(n => _ withName n),
    if (useCache) Some(_.withCache) else None,
    timeout.map(t => _ withTimeout t)
  )

  stages.flatten.reduceLeft(_ andThen _)(builder)
}

【讨论】:

  • 太棒了!非常感谢,这里当然是值得探索的好主意!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-23
  • 1970-01-01
  • 1970-01-01
  • 2016-04-10
  • 2014-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多