Without library support, you can roll out your own Boolean Reader Monad.
This is functional, pure and configurable.
Boolean Reader
case class BoolConf[A](run: Boolean => A) {
def apply(b: Boolean) = run(b)
def map[B](f: A => B): BoolConf[B] = BoolConf(b => f(run(b)))
def flatMap[B](f: A => BoolConf[B]): BoolConf[B] = BoolConf(b => f(run(b))(b))
}
Here we made a wrapper for a Boolean => A that allows monadic composition, and it's probably already implemented in some library, like scalaz.
We're only interested in the run method, for this case, but you can get interested in other opportunities.
Configured filter & sort
Then we wrap our filter and sort check with the Reader
val mFilter: Seq[SomeClass] => BoolConf[Seq[SomeClass]] = seq => BoolConf(if(_) seq.filter(...) else seq)
val mSort: Seq[SomeClass] => BoolConf[Seq[SomeClass]] = seq => BoolConf(if(_) seq.sortWith(...) else seq)
Lifting
Now, to compose these functions, since the output is no more a simple Seq, we need to lift one of them to work within a BoolConf
def lift2Bool[A, B]: (A => B) => (BoolConf[A] => BoolConf[B]) =
fun => cfg => BoolConf(bool => fun(cfg(bool)))
Now we're able to convert any function from A => B to a lifted function from BoolConf[A] => BoolConf[B]
Composing
Now we can compose functionally:
val filterAndSort = lift2Bool(mSort) compose mFilter
//or the equivalent
val filterAndSort = mFilter andThen lift2Bool(mSort)
//applies as in filterAndSort(<sequence>)(<do filter>)(<do sort>)
There's more
We can also create a generic "builder" for our mFilter and mSort
val configFilter[SomeClass]: (SomeClass => Boolean) => Seq[MyClass] => BoolConf[Seq[SomeClass]] =
filterer => seq => BoolConf(if(_) seq.filter(filterer))
You can "sort" the sorting equivalent by yourself
Thanks are due to Runar for the inspiration