I would like to suggest a new method to be added to scala.util.ChainingOps, called pipeIf
(see also https://stackoverflow.com/q/65955699/16673).
We use following implementation in our codebase a lot:
final implicit class ChainingOpsExt[A](private val self: A) {
def pipeIf[B >: A](cond: Boolean)(f: A => B): B = if (cond) f(self) else self
def pipeIf[B >: A](cond: A => Boolean)(f: A => B): B = if (cond(self)) f(self) else self
}
Examples:
val lowCase = true
"AbCdE".pipeIf(lowCase)(_.toLowerCase)
"AbCdE".pipeIf(_(0).isUpper)(_.toLowerCase)
Note: The [B >: A]
is useful when the function is widening a type (returning a base class of A). It seems to work in Scala 3 only, though, not in Scala 2.13:
trait T
class AA extends T
class BB extends T
new AA().pipeIf(false)(_ => new BB) // error in Scala 2.13 - found BB, required AA
3 Likes
Without pipeIf
, one can write:
"AbCdE".pipe(x => if (lowCase) x.toLowerCase else x)
which just inlines the definition of pipeIf
that you provide.
But I’d like to note that one can also write:
"AbCdE".pipe(if (lowCase) _.toLowerCase else identity))
thereby avoiding the need to use function literal syntax and invent a parameter name.
I don’t have a strong opinion either way on your proposal, I just wanted to make this small observation.
1 Like
That’s pretty neat. Though note that it desugars to
"AbCdE".pipe(if (lowCase) x => x.toLowerCase else x => identity(x))
Similarly,
def cond[B >: A](pf: PartialFunction[A, B]): B = pf.applyOrElse(self, identity)
for
"AbCdE".cond { case s if s(0).isUpper => s.toLowerCase }
There was a recent thread about syntax of one-line cases:
x match { case C(_) => y case _ => z }
where there is no semicolon to visually separate the cases. I observed that this syntax is preferred (in scalac) when the second case is a “default”.
x match { case C(_) => true case _ => false }
PartialFunction.cond(x) { case C(_) => true }
I think this use case deserves more support. The other utility doesn’t supply a default:
PartialFunction.condOpt(x) { case C(y) => y } // Some(y) or None
where you would .getOrElse
or Option.fold
etc.