All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.eharmony.aloha.feature.OptionMath.scala Maven / Gradle / Ivy

The newest version!
package com.eharmony.aloha.feature

object OptionMath extends OptionMath

/** Provides basic inline unary and binary mathematical operators for options of primitive types including.
  *
  * - Option[Byte]
  * - Option[Short]
  * - Option[Int]
  * - Option[Long]
  * - Option[Float]
  * - Option[Double]
  *
  * Additionally, operations are provided that can take any scala.math.Numeric type as long as an implicit of that
  * type is available at call time.
  *
  * This functionality assumes that the type arguments are the same as it relies on scala.math.Numeric.  When the types
  * are the same, everything works seamlessly.
  *
  * {{{
  * import com.eharmony.aloha.feature.OptionMath.Syntax._
  *
  * scala> Option(1) + None
  * res0: Option[Int] = None
  *
  * scala> None + Option(1)
  * res1: Option[Int] = None
  *
  * scala> Option(1) + Option(2)
  * res3: Option[Int] = Some(3)
  *
  * scala> Some(1L.toInt) + Option(2)
  * res4: Option[Int] = Some(3)
  *
  * scala> Option(1) < Option(2)
  * res5: Option[Boolean] = Some(true)
  *
  * scala> Option(1) >= Option(2)
  * res6: Option[Boolean] = Some(false)
  *
  * scala> Option(1) < None
  * res7: Option[Boolean] = None
  * }}}
  *
  * When the types for the function arguments don't line up, we get a compile time error.
  *
  * {{{
  * scala> Some(1) + Some(1.5)
  * :12: error: type mismatch;
  * found   : Double(1.5)
  * required: Int
  *              Some(1) + Some(1.5)
  *                             ^
  * }}}
  *
  * Because inline operations require the use of implicit classes, inline syntax will incur an object creation
  * overhead that using OptMathOps will not.
  *
  * {{{
  * import com.eharmony.aloha.feature.OptionMath.Syntax._
  * import com.eharmony.aloha.feature.OptionMath.OptMathOps
  *
  * val a = Option(1.5)
  * val b = Option(0.5)
  *
  * val c = a / b                 // Additional object creation involved (slower, prettier)
  * val d = OptMathOps.div(a, b)  // Less object creation involved (faster, uglier)
  *
  * assert(c == d)
  * }}}
  *
  * Notice that the usual order of operations in the same.
  *
  * {{{
  * import com.eharmony.aloha.feature.OptionMath.Syntax._
  *
  * scala> Option(10 % 4 / 2) == Option(10) % Option(4) / Option(2)
  * res0: Boolean = true
  * }}}
  */
trait OptionMath {

    sealed protected[this] trait InlineSyntax[A, NumType <: Numeric[A]] {
        protected implicit val num: NumType
        val left: Option[A]

        @inline def abs() = OptMathOps.abs(left)
        @inline def unary_- = OptMathOps.negate(left)
        @inline def +(right: Option[A]) = OptMathOps.plus(left, right)
        @inline def -(right: Option[A]) = OptMathOps.minus(left, right)
        @inline def *(right: Option[A]) = OptMathOps.times(left, right)
        def /(right: Option[A]): Option[A]

        @inline def <(right: Option[A]) = OptMathOps.lt(left, right)
        @inline def <=(right: Option[A]) = OptMathOps.lteq(left, right)
        @inline def >(right: Option[A]) = OptMathOps.gt(left, right)
        @inline def >=(right: Option[A]) = OptMathOps.gteq(left, right)
    }

    sealed protected[this] trait InlineIntegralSyntax[A] extends InlineSyntax[A, Integral[A]] {
        @inline def /(right: Option[A]) = OptMathOps.quot(left, right)
        @inline def %(right: Option[A]) = OptMathOps.rem(left, right)
    }

    sealed protected[this] trait InlineFractionalSyntax[A] extends InlineSyntax[A, Fractional[A]] {
        @inline def /(right: Option[A]) = OptMathOps.div(left, right)
    }

    /** Should not use a wildcard import to include all functions in the object.  Instead, import the OptMathOps object.
      */
    object OptMathOps {
        @inline final def abs[A](a: Option[A])(implicit n: Numeric[A]) = a.map(n.abs)
        @inline final def negate[A](a: Option[A])(implicit n: Numeric[A]) = a.map(n.negate)
        @inline final def plus[A](left: Option[A], right: Option[A])(implicit n: Numeric[A]) = for (l <- left; r <- right) yield n.plus(l, r)
        @inline final def minus[A](left: Option[A], right: Option[A])(implicit n: Numeric[A]) = for (l <- left; r <- right) yield n.minus(l, r)
        @inline final def times[A](left: Option[A], right: Option[A])(implicit n: Numeric[A]) = for (l <- left; r <- right) yield n.times(l, r)

        @inline final def quot[A](left: Option[A], right: Option[A])(implicit n: Integral[A]) = for (l <- left; r <- right) yield n.quot(l, r)
        @inline final def rem[A](left: Option[A], right: Option[A])(implicit n: Integral[A]) = for (l <- left; r <- right) yield n.rem(l, r)
        @inline final def div[A](left: Option[A], right: Option[A])(implicit n: Fractional[A]) = for (l <- left; r <- right) yield n.div(l, r)

        @inline final def lt[A](left: Option[A], right: Option[A])(implicit n: Numeric[A]) = for (l <- left; r <- right) yield n.lt(l, r)
        @inline final def lteq[A](left: Option[A], right: Option[A])(implicit n: Numeric[A]) = for (l <- left; r <- right) yield n.lteq(l, r)
        @inline final def gt[A](left: Option[A], right: Option[A])(implicit n: Numeric[A]) = for (l <- left; r <- right) yield n.gt(l, r)
        @inline final def gteq[A](left: Option[A], right: Option[A])(implicit n: Numeric[A]) = for (l <- left; r <- right) yield n.gteq(l, r)
    }

    object Syntax {
        implicit class OptionByteMathSyntax(val left: Option[Byte])(implicit protected val num: Integral[Byte]) extends InlineIntegralSyntax[Byte]
        implicit class OptionShortMathSyntax(val left: Option[Short])(implicit protected val num: Integral[Short]) extends InlineIntegralSyntax[Short]
        implicit class OptionIntMathSyntax(val left: Option[Int])(implicit protected val num: Integral[Int]) extends InlineIntegralSyntax[Int]
        implicit class OptionLongMathSyntax(val left: Option[Long])(implicit protected val num: Integral[Long]) extends InlineIntegralSyntax[Long]
        implicit class OptionFloatMathSyntax(val left: Option[Float])(implicit protected val num: Fractional[Float]) extends InlineFractionalSyntax[Float]
        implicit class OptionDoubleMathSyntax(val left: Option[Double])(implicit protected val num: Fractional[Double]) extends InlineFractionalSyntax[Double]
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy