Wai Yip Tung - 1 year ago 51

Scala Question

I have an object

`x`

`x.transformA.transformB.transformC`

However, before each transformation, I should also check for a condition. Only if the condition is true should I perform the transformation. I have two way to express this. The first is to define x as a var.

`var x = anObject`

if (x.condA) x = x.transformA

if (x.condB) x = x.transformB

if (x.condC) x = x.transformC

// final result in x

Alternatively, to avoid var, I define a series of intermediate variables

`val x0 = anObject`

val x1 = if (x0.condA) x0.transformA else x0

val x2 = if (x1.condB) x1.transformB else x1

val x3 = if (x2.condC) x2.transformC else x2

// final result in x3

This seems to be quite cumbersome, even error prone when I cut and paste the lines. Is there a more idiomatic way to express this?

Answer Source

It's possible to express a series of conditional transformations as a composition of `Function`

s. That way each transformation can be declared independently, and reused to build new tranformations.

As an example:

```
val ta: MyClass => MyClass = {case x if x.condA => x.transformA
case x => x}
val tb: MyClass => MyClass = {case x if x.condB => x.transformB
case x => x}
val tc: MyClass => MyClass = {case x if x.condC => x.transformC
case x => x}
```

Then we can compose those to form our transformation:

```
val transformation = ta andThen tb andThen tc
val transformedObj = transformation(x)
```

We can see above that we are repeating the same pattern over and over again. To avoid that, we could make a function that creates our partial functions, reducing the boilerplate code:

```
def mkTf[T](cond: T => Boolean, transf: T => T): T => T = {
case x if cond(x) => transf(x)
case x => x
}
```

Then we can rewrite our composition as:

```
val transformation = mkTf[MyClass](_.condA,_.transformA) andThen
mkTf[MyClass](_.condB,_.transformB) andThen
mkTf[MyClass](_.condC,_.transformC)
```