How to substitute a var with a val and be more functional than imperative


#1

HI, I have written a Java-like Scala program. And it doesn’t look good. It looks very imperative. In fact, I used a null. After some research, I think I should have used an Option.
calc1, calc2, calc3 are separate functions that length is calling on to get a final result.
The code below looks too verbose…

Any suggestions on how to approach the below function in a functional programming style would be helpful.

Here it is:

def length(length1: E, length2: E, lengths: List[E]): Double = {
var res: Double = 0
if (lengths.isEmpty) {
calc1(length1, length2)
}
var old: E = null
for (length <- lengths) {
if (old == null) {
res += calc1(length1, length)
} else {
res += calc2(old, length, res)
}
old = length
}
res += calc3(old, length2, res)
res

}

Thanks for everyone’s time.


#2

First, I’m assuming calc1/2/3 are all pure functions (i.e. have no side effects, but only calculate and return a Double). If this is not the case, you should provide their implementation.
In your code, you probably want everything after the first if in an else block, otherwise the code in the if will never have an effect.

About the main loop:
You are reducing a list of elements to a single value, using an accumulation rule. This is a typical use case for a fold operation. The foldLeft method on list takes a start value and a function that generates the next value from the current value and current list element, going through the whole list this way.
For example, to sum a list of integers, you can write

List(1,2,3).foldLeft(0)((acc, i) => acc + i)

Starting with value 0, the function is called with that value and the next list element to give the sum of all the values in the list.
For your method, it get’s a bit more complicated, as you have to handle the first list element differently. Also, you need two intermediate values (old and res).

So as start value, you can use a tuple, consisting of the first list element and the result of the calc1 method. Your fold function will then receive a Double and an E as accumulator value.

val startValue = (lengths.head, calc1(length1, lengths.head))

As we now have the first value already, the fold function should not handle it again, so we use lengths.tail.foldLeft(...): tail gives the list with the head removed.
In the function passed to foldLeft, your calc2 method must be called. But as our start value is a tuple of (E, Double), we also must return such a tuple, so we always also pass on the current length.

(acc, length) =>
  // acc is the tuple of previous length and calculation result
  acc match {
    //pass on current element and calc result
    case (old, acc) => (length, calc2(old, length, acc)) 
  }

The result of the fold will be a tuple with the last element and the value of your res after your loop. So we have everything we need to call calc3. Your last two lines can be made functional by simply removing the assignment and the second line:

res += calc3(old, length2, res)
res
// is same as returning result of summing directly:
res + calc3(old, length2, res)

Putting it all together:

def length(length1: E, length2: E, lengths: List[E]): Double = {
  if (lengths.isEmpty) {
    calc1(length1, length2)
  } else {
    val startValue = (lengths.head, calc1(length1, lengths.head))
    val (last, res) = lengths.tail.foldLeft(startValue) { (acc, length) =>
      acc match {
        case (old, acc) => (length, calc2(old, length, acc))
      }
    }
    res + calc3(last, length2, res)
  }
}

Now, depending on what your calc methods do, this could be made simpler. So if you post your E type and the calc methods, I’ll take a look.


#3

calc1/2/3 are all pure functions. They all just return a Double only. I will post the E Type and calc methods…
Thank you for the detailed analysis so far.


#4

Hi
I confess I did not understand your request to post my E type.
What am I missing here?


#5

What kind of value is ‘E’? Your function declares the variables length1 and length2 as type ‘E’, but we have no visibility into what the means. Is ‘E’ an alias, a generic type, or something else (e.g., a class)?


#6

E is a Double then.


#7

Okay, at this point, E is just a Double, but i am going to have another subinterface like MetricLength