2017-07-03 41 views
3

我有一个函数,如下所示:应用所有过滤器的功能为一个值

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

不幸的是,我无法弄清楚如何定义一个有意义。或者,如果有更好的/不同的方式来做到这一点?

我使用的猫,如果该事项。有任何想法吗?

+0

*在没有的情况下返回身份*返回身份(。零)的幺半群?意思是如果你有一个'Monoid [String] .zero'你会返回一个空字符串,那好吗? –

+0

@YuvalItzchakov嗯,我敢肯定,我的意思是应用'identity'函数返回初始'builder' ... –

+0

啊,所以你在谈论一个'含半幺群[生成器]'? –

回答

1

我觉得你的情况A => M[A]结构是有点画蛇添足。您在示例中使用的过滤器功能实际上等同于Option[Builder => Builder]。这是因为您不使用他们的参数来决定结果应该是Some还是None。您可以使用.getOrElse(identity)将功能进一步简化为Builder => Builder

这里有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) 
} 

另一种可能性是flattenOptionList的s,这只会删除None•不用他们强迫到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) 
} 
+0

令人惊叹!非常感谢,这里值得探索的伟大创意! –