Convert a binary function of Double to binary function of Option[Double]

I have a curry-able function for comparing Doubles

  def almostEqual(tolerance:Double)(x:Double, y:Double):Boolean = {
    ( x == y) ||  abs(x - y ) < tolerance * max(abs(x),abs(y))
  }

I often call the function like almostEqual(0.01) to return me a binary function for testing whether two given Doubles are within 1 percent of each other.

In several cases I have Option[Double] rather than Double. So I wrote the following function, so I can use ae(0.01) to give me a function of type (Option[Double],Option[Double])=>Boolean which will be a function to determine whether the two underlying numbers are within 1 percent of each other.

My gut feel is that there is a much easier way to do this. What is the best way to write the function which takes almostEqual as argument and returns something isomorphic to ae ?

    def optionAlmostEqual(test:(Double,Double)=>Boolean):(Option[Double],Option[Double])=>Boolean = {
      (op1,op2) => {
        (op1,op2) match {
          case (Some(a),Some(b)) => test(a,b)
          case _ => false
        }
      }
    }
    def ae(delta:Double):(Option[Double],Option[Double])=>Boolean = {
      optionAlmostEqual(almostEqual(delta))
    }

Is the following better?

def testOp(test: (Double, Double) => Boolean)(
       fst: Option[Double], snd : Option[Double]
) = {
  for {
    x<- fst
    y <- snd
  } yield test(x, y)}.getOrElse(false) 

best
Siddhartha

I was wondering whether it could be done with something like almostEqual.flatMap.tupled.getOrElse(false) But that’s just an intuition.

You can shorten the code using scalaz library and its wealth of weird looking operators (I don’t recommend it, though).

  // original solution
  def optionAlmostEqual1(test: (Double, Double) => Boolean)
    : (Option[Double], Option[Double]) => Boolean = (op1, op2) => {
    (op1, op2) match {
      case (Some(a), Some(b)) => test(a, b)
      case _                  => false
    }
  }

  // little refactored @siddhartha-gadgil solution
  def optionAlmostEqual2(test: (Double, Double) => Boolean)
    : (Option[Double], Option[Double]) => Boolean = (o1, o2) => {
    (for (d1 <- o1; d2 <- o2) yield test(d1, d2)).getOrElse(false)
  }

  // solution using scalaz library
  def optionAlmostEqual3(test: (Double, Double) => Boolean)
    : (Option[Double], Option[Double]) => Boolean = (o1, o2) => {
    import scalaz._
    import Scalaz.{test => _, _}
    (o1 |@| o2)(test) | false
  }
1 Like

Don’t use the Applicative builder. Just use Apply

import scalaz.Apply
Apply[Option].apply2(maybeDouble1, maybeDouble2)(test).getOrElse(false)

The Cats library also has an Apply with slightly different naming.

1 Like