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

com.twitter.algebird.Monoid.scala Maven / Gradle / Ivy

There is a newer version: 0.12.2
Show newest version
/*
Copyright 2012 Twitter, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.twitter.algebird

import scala.annotation.implicitNotFound
import scala.math.Equiv
import scala.reflect.ClassTag

import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool }
import java.util.{ List => JList, Map => JMap }

import scala.collection.mutable.{ Map => MMap }
import scala.collection.{ Map => ScMap }

/**
 * Monoid (take a deep breath, and relax about the weird name):
 *   This is a semigroup that has an additive identity (called zero), such that a+0=a, 0+a=a, for every a
 */

@implicitNotFound(msg = "Cannot find Monoid type class for ${T}")
trait Monoid[@specialized(Int, Long, Float, Double) T] extends Semigroup[T] {
  def zero: T //additive identity
  def isNonZero(v: T): Boolean = (v != zero)
  def assertNotZero(v: T) {
    if (!isNonZero(v)) {
      throw new java.lang.IllegalArgumentException("argument should not be zero")
    }
  }

  def nonZeroOption(v: T): Option[T] = {
    if (isNonZero(v)) {
      Some(v)
    } else {
      None
    }
  }
  // Override this if there is a more efficient means to implement this
  def sum(vs: TraversableOnce[T]): T = sumOption(vs).getOrElse(zero)
}

// For Java interop so they get the default methods
abstract class AbstractMonoid[T] extends Monoid[T]

/**
 * Some(5) + Some(3) == Some(8)
 * Some(5) + None == Some(5)
 */
class OptionMonoid[T](implicit semi: Semigroup[T]) extends Monoid[Option[T]] {
  def zero = None
  def plus(left: Option[T], right: Option[T]): Option[T] = {
    if (left.isEmpty) {
      right
    } else if (right.isEmpty) {
      left
    } else {
      Some(semi.plus(left.get, right.get))
    }
  }
  override def sumOption(items: TraversableOnce[Option[T]]): Option[Option[T]] =
    if (items.isEmpty) None
    else Some(semi.sumOption(items.filter(_.isDefined).map { _.get }))
}

class EitherMonoid[L, R](implicit semigroupl: Semigroup[L], monoidr: Monoid[R]) extends EitherSemigroup[L, R]()(semigroupl, monoidr) with Monoid[Either[L, R]] {
  override lazy val zero = Right(monoidr.zero)
}

object StringMonoid extends Monoid[String] {
  override val zero = ""
  override def plus(left: String, right: String) = left + right
  override def sumOption(items: TraversableOnce[String]): Option[String] =
    if (items.isEmpty) None
    else Some(items.mkString(""))
}

/**
 * List concatenation monoid.
 * plus means concatenation, zero is empty list
 */
class ListMonoid[T] extends Monoid[List[T]] {
  override def zero = List[T]()
  override def plus(left: List[T], right: List[T]) = left ++ right
  override def sumOption(items: TraversableOnce[List[T]]): Option[List[T]] =
    if (items.isEmpty) None
    else {
      // ListBuilder mutates the tail of the list until
      // result is called so that it is O(N) to push N things on, not N^2
      val builder = List.newBuilder[T]
      items.foreach { builder ++= _ }
      Some(builder.result())
    }
}

// equivalent to ListMonoid
class SeqMonoid[T] extends Monoid[Seq[T]] {
  override def zero = Seq[T]()
  override def plus(left: Seq[T], right: Seq[T]) = left ++ right
  override def sumOption(items: TraversableOnce[Seq[T]]): Option[Seq[T]] =
    if (items.isEmpty) None
    else {
      val builder = Seq.newBuilder[T]
      items.foreach { builder ++= _ }
      Some(builder.result())
    }
}

/**
 * Pair-wise sum Array monoid.
 *
 * plus returns left[i] + right[i] for all array elements.
 * The resulting array will be as long as the longest array (with its elements duplicated)
 * zero is an empty array
 */
class ArrayMonoid[T: ClassTag](implicit semi: Semigroup[T]) extends Monoid[Array[T]] {

  //additive identity
  override def isNonZero(v: Array[T]): Boolean = v.nonEmpty

  override def zero = Array[T]()
  override def plus(left: Array[T], right: Array[T]) = {
    val (longer, shorter) = if (left.length > right.length) (left, right) else (right, left)
    val sum = longer.clone
    for (i <- 0 until shorter.length)
      sum.update(i, semi.plus(sum(i), shorter(i)))

    sum
  }
}

/**
 * Set union monoid.
 * plus means union, zero is empty set
 */
class SetMonoid[T] extends Monoid[Set[T]] {
  override def zero = Set[T]()
  override def plus(left: Set[T], right: Set[T]) = left ++ right
  override def sumOption(items: TraversableOnce[Set[T]]): Option[Set[T]] =
    if (items.isEmpty) None
    else {
      val mutable = scala.collection.mutable.Set[T]()
      items.foreach { s => mutable ++= s }
      Some(mutable.toSet)
    }
}

/**
 * Function1 monoid.
 * plus means function composition, zero is the identity function
 */
class Function1Monoid[T] extends Monoid[Function1[T, T]] {
  override def zero = identity[T]

  // (f1 + f2)(x) = f2(f1(x)) so that:
  // listOfFn.foldLeft(x) { (v, fn) => fn(v) } = (Monoid.sum(listOfFn))(x)
  override def plus(f1: Function1[T, T], f2: Function1[T, T]) = {
    (t: T) => f2(f1(t))
  }
}

// To use the OrValMonoid wrap your item in a OrVal object
case class OrVal(get: Boolean) extends AnyVal

object OrVal {
  implicit def monoid: Monoid[OrVal] = OrValMonoid
  def unboxedMonoid: Monoid[Boolean] = new Monoid[Boolean] {
    def zero = false
    def plus(l: Boolean, r: Boolean) = l || r
    override def sumOption(its: TraversableOnce[Boolean]) =
      if (its.isEmpty) None
      else Some(its.exists(identity))
  }
}

/**
 * Boolean OR monoid.
 * plus means logical OR, zero is false.
 */
object OrValMonoid extends Monoid[OrVal] {
  override def zero = OrVal(false)
  override def plus(l: OrVal, r: OrVal) = if (l.get) l else r
  override def sumOption(its: TraversableOnce[OrVal]) =
    if (its.isEmpty) None
    else Some(OrVal(its.exists(_.get)))
}

// To use the AndValMonoid wrap your item in a AndVal object
case class AndVal(get: Boolean) extends AnyVal

object AndVal {
  implicit def monoid: Monoid[AndVal] = AndValMonoid
  def unboxedMonoid: Monoid[Boolean] = new Monoid[Boolean] {
    def zero = true
    def plus(l: Boolean, r: Boolean) = l && r
    override def sumOption(its: TraversableOnce[Boolean]) =
      if (its.isEmpty) None
      else Some(its.forall(identity))
  }
}

/**
 * Boolean AND monoid.
 * plus means logical AND, zero is true.
 */
object AndValMonoid extends Monoid[AndVal] {
  override def zero = AndVal(true)
  override def plus(l: AndVal, r: AndVal) = if (l.get) r else l
  override def sumOption(its: TraversableOnce[AndVal]) =
    if (its.isEmpty) None
    else Some(AndVal(its.forall(_.get)))
}

object Monoid extends GeneratedMonoidImplicits with ProductMonoids {
  // This pattern is really useful for typeclasses
  def zero[T](implicit mon: Monoid[T]) = mon.zero
  // strictly speaking, same as Semigroup, but most interesting examples
  // are monoids, and code already depends on this:
  def plus[T](l: T, r: T)(implicit monoid: Monoid[T]): T = monoid.plus(l, r)
  def assertNotZero[T](v: T)(implicit monoid: Monoid[T]) = monoid.assertNotZero(v)
  def isNonZero[T](v: T)(implicit monoid: Monoid[T]) = monoid.isNonZero(v)
  def nonZeroOption[T](v: T)(implicit monoid: Monoid[T]) = monoid.nonZeroOption(v)
  // Left sum: (((a + b) + c) + d)
  def sum[T](iter: TraversableOnce[T])(implicit monoid: Monoid[T]): T =
    monoid.sum(iter)

  def from[T](z: => T)(associativeFn: (T, T) => T): Monoid[T] = new Monoid[T] {
    lazy val zero = z
    def plus(l: T, r: T) = associativeFn(l, r)
  }

  /**
   * Return an Equiv[T] that uses isNonZero to return equality for all zeros
   * useful for Maps/Vectors that have many equivalent in memory representations of zero
   */
  def zeroEquiv[T: Equiv: Monoid]: Equiv[T] = Equiv.fromFunction { (a: T, b: T) =>
    (!isNonZero(a) && !isNonZero(b)) || Equiv[T].equiv(a, b)
  }

  /**
   * Same as v + v + v .. + v (i times in total)
   * requires i >= 0, wish we had NonnegativeBigInt as a class
   */
  def intTimes[T](i: BigInt, v: T)(implicit mon: Monoid[T]): T = {
    require(i >= 0, "Cannot do negative products with a Monoid, try Group.intTimes")
    if (i == 0) {
      mon.zero
    } else {
      Semigroup.intTimes(i, v)(mon)
    }
  }

  implicit val nullMonoid: Monoid[Null] = NullGroup
  implicit val unitMonoid: Monoid[Unit] = UnitGroup
  implicit val boolMonoid: Monoid[Boolean] = BooleanField
  implicit val jboolMonoid: Monoid[JBool] = JBoolField
  implicit val intMonoid: Monoid[Int] = IntRing
  implicit val jintMonoid: Monoid[JInt] = JIntRing
  implicit val shortMonoid: Monoid[Short] = ShortRing
  implicit val jshortMonoid: Monoid[JShort] = JShortRing
  implicit val bigIntMonoid: Monoid[BigInt] = BigIntRing
  implicit val longMonoid: Monoid[Long] = LongRing
  implicit val jlongMonoid: Monoid[JLong] = JLongRing
  implicit val floatMonoid: Monoid[Float] = FloatField
  implicit val jfloatMonoid: Monoid[JFloat] = JFloatField
  implicit val doubleMonoid: Monoid[Double] = DoubleField
  implicit val jdoubleMonoid: Monoid[JDouble] = JDoubleField
  implicit val stringMonoid: Monoid[String] = StringMonoid
  implicit def optionMonoid[T: Semigroup]: Monoid[Option[T]] = new OptionMonoid[T]
  implicit def listMonoid[T]: Monoid[List[T]] = new ListMonoid[T]
  implicit def seqMonoid[T]: Monoid[Seq[T]] = new SeqMonoid[T]
  implicit def arrayMonoid[T: ClassTag](implicit semi: Semigroup[T]) = new ArrayMonoid[T]
  implicit def indexedSeqMonoid[T: Monoid]: Monoid[IndexedSeq[T]] = new IndexedSeqMonoid[T]
  implicit def jlistMonoid[T]: Monoid[JList[T]] = new JListMonoid[T]
  implicit def setMonoid[T]: Monoid[Set[T]] = new SetMonoid[T]
  implicit def mapMonoid[K, V: Semigroup]: Monoid[Map[K, V]] = new MapMonoid[K, V]
  implicit def scMapMonoid[K, V: Semigroup]: Monoid[ScMap[K, V]] = new ScMapMonoid[K, V]
  implicit def jmapMonoid[K, V: Semigroup]: Monoid[JMap[K, V]] = new JMapMonoid[K, V]
  implicit def eitherMonoid[L: Semigroup, R: Monoid]: Monoid[Either[L, R]] = new EitherMonoid[L, R]
  implicit def function1Monoid[T]: Monoid[Function1[T, T]] = new Function1Monoid[T]
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy