2
\$\begingroup\$

The following code has a lot of conditionals. I am trying to write it in a functional programming way.

val basePrice = {
  var b = 0.0

  if (runtime > 120)
    b += 1.5
  if ((day == Sat) || (day == Sun))
    b += 1.5
  if (!isParquet)
    b += 2
  if (is3D)
    b += 3
  b
}

I think the following code would be a good approach, but maybe I am complicating this too much.

val basePrice = {
  List((runtime > 120, 1.5), 
       (day == Sat || day == Sun, 1.5),
       (!isParquet, 2.0),
       (is3D, 3.0)).foldLeft(0.0)((acum, cond) =>
     if (cond._1) acum + cond._2 else acum)
}

How would you write the first snippet of code using functional programming?

\$\endgroup\$

4 Answers 4

9
\$\begingroup\$

Each condition is a function. It might be that you could write it more concisely, but I think the code is clearer if you do this:

def priceFunction(cond: => Boolean)(mod: Double => Double) = (_: Double) match {
    case x if cond => mod(x)
    case y => y
}

val modRuntime = priceFunction(runtime > 120)(_ + 1.5)
val modWeekend = priceFunction(day == Sat || day == Sun)(_ + 1.5)
val modParquet = priceFunction(!isParquet)(_ + 2.0)
val mod3d = priceFunction(is3D)(_ + 3.0)
val modifiers = List(
    modRuntime,
    modWeekend,
    modParquet,
    mod3d
)
val modifierFunction = modifiers reduceLeft (_ andThen _)                   

val basePrice = modifierFunction(0.0)

The name of the identifiers here suck, and I could have written val modifiers = modRuntime andThen modWeekend andThen modParquet andThen mod3d without trouble. I choose putting them in a List because it shows how well it can scale.

One could also make PartialFunction and chain them with orElse, for the cases where you want only the first condition.

You see this kind of thing used in web frameworks, such as BlueEyes, Lift or Unfiltered, for example.

\$\endgroup\$
2
  • \$\begingroup\$ You've littered the code with one-shot vals, things which are unlikely to be directly re-used. Why not create a list of anonymous functions, at least? "modifiers = List( priceFunction(runtime > 120)(_ + 1.5), priceFunction(day == Sat || day == Sun)(_ + 1.5). etc, etc )". Frankly, I think simao's second example is as good as yours. \$\endgroup\$
    – itsbruce
    Aug 24, 2013 at 17:15
  • 1
    \$\begingroup\$ @itsbruce For two reasons. First, the identifier name helps one understand what the function is doing. Second, functions are syntactically noisy, so it's difficult to see where one ends and another starts in a list, whereas the val declarations make that obvious. The main question is: why not declare them as val? It's not as if there would be any difference in memory usage or performance. \$\endgroup\$ Aug 25, 2013 at 5:33
2
\$\begingroup\$

Maybe a little bit more readable:

val basePrice = List((runtime > 120, 1.5), 
       (day == Sat || day == Sun, 1.5),
       (!isParquet, 2.0),
       (is3D, 3.0)).collect{case (true, b) => b}.sum
\$\endgroup\$
3
  • \$\begingroup\$ Scala noob here. Would this cause the list to be traversed twice? One for collect and another for sum? Or scala is aware of that and chains it properly? \$\endgroup\$
    – simao
    Aug 17, 2011 at 23:24
  • \$\begingroup\$ I think it would be traversed twice, but I really hope you don't want to chain hundreds of these... \$\endgroup\$
    – Landei
    Aug 18, 2011 at 6:46
  • \$\begingroup\$ Note that you can use .view on lists to make them lazy (and, hence, traversed once). \$\endgroup\$ Sep 14, 2011 at 5:02
0
\$\begingroup\$

I like Daniel C. Sobral's answer. Anyway, in addition to the List(...).sum alternatives, there's:

val basePrice = {
  0.0 +
    (runtime > 120 ? 1.5 | 0) +
    ((day == Sat) || (day == Sun) ? 1.5 | 0) +
    (!isParquet ? 2 | 0) +
    (is3D ? 3 | 0)
}

Where ?| is the ternary operator.

\$\endgroup\$
0
\$\begingroup\$

You can simply write it like this:

val basePrice = 0.0 +
  (if (runtime > 120) 1.5 else 0) +
  (if (day == Sat || day == Sun) 1.5 else 0) +
  (if (!isParquet) 2 else 0) +
  (if (is3D) 3 else 0)
\$\endgroup\$
1
  • \$\begingroup\$ This is not more functional, just a slightly different way of writing the same code. \$\endgroup\$
    – itsbruce
    Aug 24, 2013 at 16:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.