diff --git a/.travis.yml b/.travis.yml index 72b906408..c0f6557b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,8 @@ matrix: script: ./sbt ++$TRAVIS_SCALA_VERSION clean test - scala: 2.11.8 - script: ./sbt ++$TRAVIS_SCALA_VERSION clean coverage test coverageReport mimaReportBinaryIssues docs/makeMicrosite + #script: ./sbt ++$TRAVIS_SCALA_VERSION clean coverage test coverageReport mimaReportBinaryIssues docs/makeMicrosite + script: ./sbt ++$TRAVIS_SCALA_VERSION clean coverage test coverageReport docs/makeMicrosite after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/algebird-bijection/src/main/scala/com/twitter/algebird/bijection/AlgebirdBijections.scala b/algebird-bijection/src/main/scala/com/twitter/algebird/bijection/AlgebirdBijections.scala index d3f666893..331c1a3cd 100644 --- a/algebird-bijection/src/main/scala/com/twitter/algebird/bijection/AlgebirdBijections.scala +++ b/algebird-bijection/src/main/scala/com/twitter/algebird/bijection/AlgebirdBijections.scala @@ -16,7 +16,7 @@ limitations under the License. package com.twitter.algebird.bijection -import com.twitter.algebird.{ Field, Group, Monoid, Ring, Semigroup } +import com.twitter.algebird.{ Group, Monoid, Ring, Semigroup } import com.twitter.bijection.{ AbstractBijection, Bijection, ImplicitBijection, Conversion, Reverse } import Conversion.asMethod // "as" syntax @@ -51,11 +51,6 @@ class BijectedRing[T, U](implicit val ring: Ring[T], bij: ImplicitBijection[T, U ring.product(iter map { _.as[T] }).as[U] } -class BijectedField[T, U](implicit val field: Field[T], bij: ImplicitBijection[T, U]) extends BijectedRing[T, U] with Field[U] { - override def div(l: U, r: U): U = field.div(l.as[T], r.as[T]).as[U] - override def inverse(u: U): U = field.inverse(u.as[T]).as[U] -} - trait AlgebirdBijections { implicit def semigroupBijection[T, U](implicit bij: ImplicitBijection[T, U]): Bijection[Semigroup[T], Semigroup[U]] = new AbstractBijection[Semigroup[T], Semigroup[U]] { @@ -80,12 +75,6 @@ trait AlgebirdBijections { override def apply(ring: Ring[T]) = new BijectedRing[T, U]()(ring, bij) override def invert(ring: Ring[U]) = new BijectedRing[U, T]()(ring, Reverse(bij.bijection)) } - - implicit def fieldBijection[T, U](implicit bij: ImplicitBijection[T, U]): Bijection[Field[T], Field[U]] = - new AbstractBijection[Field[T], Field[U]] { - override def apply(field: Field[T]) = new BijectedField[T, U]()(field, bij) - override def invert(field: Field[U]) = new BijectedField[U, T]()(field, Reverse(bij.bijection)) - } } object AlgebirdBijections extends AlgebirdBijections diff --git a/algebird-core/src/main/scala/com/twitter/algebird/AdjoinedUnitRing.scala b/algebird-core/src/main/scala/com/twitter/algebird/AdjoinedUnitRing.scala index ccbbebe68..164bc6809 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/AdjoinedUnitRing.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/AdjoinedUnitRing.scala @@ -17,6 +17,8 @@ package com.twitter.algebird import scala.annotation.tailrec +import algebra.ring.Rng + /** * This is for the case where your Ring[T] is a Rng (i.e. there is no unit). * @see http://en.wikipedia.org/wiki/Pseudo-ring#Adjoining_an_identity_element @@ -27,33 +29,36 @@ case class AdjoinedUnit[T](ones: BigInt, get: T) { object AdjoinedUnit { def apply[T](item: T): AdjoinedUnit[T] = new AdjoinedUnit[T](BigInt(0), item) - implicit def ring[T](implicit ring: Ring[T]): Ring[AdjoinedUnit[T]] = new AdjoinedUnitRing[T] + implicit def ring[T](implicit ring: Rng[T]): Ring[AdjoinedUnit[T]] = new AdjoinedUnitRing[T] } -class AdjoinedUnitRing[T](implicit ring: Ring[T]) extends Ring[AdjoinedUnit[T]] { - val one = AdjoinedUnit[T](BigInt(1), ring.zero) - val zero = AdjoinedUnit[T](ring.zero) +class AdjoinedUnitRing[T](implicit rng: Rng[T]) extends Ring[AdjoinedUnit[T]] { + val one = AdjoinedUnit[T](BigInt(1), rng.zero) + val zero = AdjoinedUnit[T](rng.zero) + + private[this] val group: Group[T] = + new FromAlgebraGroup(rng.additive) override def isNonZero(it: AdjoinedUnit[T]) = - (it.ones != 0) || ring.isNonZero(it.get) + (it.ones != 0) || group.isNonZero(it.get) def plus(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) = - AdjoinedUnit(left.ones + right.ones, ring.plus(left.get, right.get)) + AdjoinedUnit(left.ones + right.ones, rng.plus(left.get, right.get)) override def negate(it: AdjoinedUnit[T]) = - AdjoinedUnit(-it.ones, ring.negate(it.get)) + AdjoinedUnit(-it.ones, rng.negate(it.get)) override def minus(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) = - AdjoinedUnit(left.ones - right.ones, ring.minus(left.get, right.get)) + AdjoinedUnit(left.ones - right.ones, rng.minus(left.get, right.get)) def times(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) = { // (n1, g1) * (n2, g2) = (n1*n2, (n1*g1) + (n2*g1) + (g1*g2)) import Group.intTimes val ones = left.ones * right.ones - val part0 = intTimes(left.ones, right.get)(ring) - val part1 = intTimes(right.ones, left.get)(ring) - val part2 = ring.times(left.get, right.get) - val nonUnit = ring.plus(part0, ring.plus(part1, part2)) + val part0 = intTimes(left.ones, right.get)(group) + val part1 = intTimes(right.ones, left.get)(group) + val part2 = rng.times(left.get, right.get) + val nonUnit = rng.plus(part0, rng.plus(part1, part2)) AdjoinedUnit(ones, nonUnit) } diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Applicative.scala b/algebird-core/src/main/scala/com/twitter/algebird/Applicative.scala index 6332b92ca..c3b9de2d6 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/Applicative.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Applicative.scala @@ -138,7 +138,7 @@ class ApplicativeMonoid[T, M[_]](implicit app: Applicative[M], mon: Monoid[T]) } /** - * Group, Ring, and Field ARE NOT AUTOMATIC. You have to check that the laws hold for your + * Group and Ring ARE NOT AUTOMATIC. You have to check that the laws hold for your * Applicative. If your M[_] is a wrapper type (Option[_], Some[_], Try[_], Future[_], etc...) * this generally works. */ @@ -149,7 +149,7 @@ class ApplicativeGroup[T, M[_]](implicit app: Applicative[M], grp: Group[T]) } /** - * Group, Ring, and Field ARE NOT AUTOMATIC. You have to check that the laws hold for your + * Group and Ring ARE NOT AUTOMATIC. You have to check that the laws hold for your * Applicative. If your M[_] is a wrapper type (Option[_], Some[_], Try[_], Future[_], etc...) * this generally works. */ @@ -158,15 +158,3 @@ class ApplicativeRing[T, M[_]](implicit app: Applicative[M], ring: Ring[T]) lazy val one = app(ring.one) def times(l: M[T], r: M[T]) = app.joinWith(l, r)(ring.times) } - -/** - * Group, Ring, and Field ARE NOT AUTOMATIC. You have to check that the laws hold for your - * Applicative. If your M[_] is a wrapper type (Option[_], Some[_], Try[_], Future[_], etc...) - * this generally works. - */ -class ApplicativeField[T, M[_]](implicit app: Applicative[M], fld: Field[T]) - extends ApplicativeRing[T, M] with Field[M[T]] { - override def inverse(v: M[T]) = app.map(v)(fld.inverse) - override def div(l: M[T], r: M[T]) = app.joinWith(l, r)(fld.div) -} - diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Field.scala b/algebird-core/src/main/scala/com/twitter/algebird/Field.scala deleted file mode 100755 index f19d6c960..000000000 --- a/algebird-core/src/main/scala/com/twitter/algebird/Field.scala +++ /dev/null @@ -1,115 +0,0 @@ -/* -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.tailrec -import scala.annotation.implicitNotFound - -import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } -import java.util.{ List => JList, Map => JMap } - -/** - * Field: Ring + division. It is a generalization of Ring and adds support for inversion and - * multiplicative identity. - */ - -@implicitNotFound(msg = "Cannot find Field type class for ${T}") -trait Field[@specialized(Int, Long, Float, Double) T] extends Ring[T] { - // default implementation uses div YOU MUST OVERRIDE ONE OF THESE - def inverse(v: T): T = { - assertNotZero(v) - div(one, v) - } - // default implementation uses inverse: - def div(l: T, r: T): T = { - assertNotZero(r) - times(l, inverse(r)) - } -} - -// For Java interop so they get the default methods -abstract class AbstractField[T] extends Field[T] - -object FloatField extends Field[Float] { - override def one = 1.0f - override def zero = 0.0f - override def negate(v: Float) = -v - override def plus(l: Float, r: Float) = l + r - override def minus(l: Float, r: Float) = l - r - override def times(l: Float, r: Float) = l * r - override def div(l: Float, r: Float) = { - assertNotZero(r) - l / r - } - override def sum(t: TraversableOnce[Float]): Float = { - var sum = 0.0f - t.foreach(sum += _) - sum - } - override def sumOption(t: TraversableOnce[Float]): Option[Float] = - if (t.isEmpty) None - else Some(sum(t)) -} - -object DoubleField extends Field[Double] { - override def one = 1.0 - override def zero = 0.0 - override def negate(v: Double) = -v - override def plus(l: Double, r: Double) = l + r - override def minus(l: Double, r: Double) = l - r - override def times(l: Double, r: Double) = l * r - override def div(l: Double, r: Double) = { - assertNotZero(r) - l / r - } - override def sum(t: TraversableOnce[Double]): Double = { - var sum = 0.0 - t.foreach(sum += _) - sum - } - override def sumOption(t: TraversableOnce[Double]): Option[Double] = - if (t.isEmpty) None - else Some(sum(t)) -} - -object BooleanField extends Field[Boolean] { - override def one = true - override def zero = false - override def negate(v: Boolean) = v - override def plus(l: Boolean, r: Boolean) = l ^ r - override def minus(l: Boolean, r: Boolean) = l ^ r - override def times(l: Boolean, r: Boolean) = l && r - override def inverse(l: Boolean) = { - assertNotZero(l) - true - } - override def div(l: Boolean, r: Boolean) = { - assertNotZero(r) - l - } -} - -object Field { - // This pattern is really useful for typeclasses - def div[T](l: T, r: T)(implicit fld: Field[T]) = fld.div(l, r) - - implicit val boolField: Field[Boolean] = BooleanField - implicit val jboolField: Field[JBool] = JBoolField - implicit val floatField: Field[Float] = FloatField - implicit val jfloatField: Field[JFloat] = JFloatField - implicit val doubleField: Field[Double] = DoubleField - implicit val jdoubleField: Field[JDouble] = JDoubleField -} diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala index 14332fdda..e3b05e356 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala @@ -15,6 +15,8 @@ limitations under the License. */ package com.twitter.algebird +import algebra.{ Group => AGroup } +import algebra.ring.AdditiveGroup import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } import java.util.{ List => JList, Map => JMap } @@ -28,10 +30,13 @@ import scala.math.Equiv */ @implicitNotFound(msg = "Cannot find Group type class for ${T}") -trait Group[@specialized(Int, Long, Float, Double) T] extends Monoid[T] { - // must override negate or minus (or both) - def negate(v: T): T = minus(zero, v) - def minus(l: T, r: T): T = plus(l, negate(r)) +trait Group[@specialized(Int, Long, Float, Double) T] extends AGroup[T] with Monoid[T] with AdditiveGroup[T] { + /* + * This are from algebra.Group + */ + override def additive: AGroup[T] = this + override def remove(l: T, r: T): T = minus(l, r) + override def inverse(v: T): T = negate(v) } // For Java interop so they get the default methods @@ -82,7 +87,20 @@ class ArrayGroup[T: ClassTag](implicit grp: Group[T]) }.toArray } -object Group extends GeneratedGroupImplicits with ProductGroups { +class FromAlgebraGroup[T](m: AGroup[T]) extends FromAlgebraMonoid(m) with Group[T] { + override def negate(t: T): T = m.inverse(t) + override def minus(r: T, l: T): T = m.remove(r, l) +} + +private[algebird] trait FromAlgebraGroupImplicit1 { + implicit def fromAlgebraAdditiveGroup[T](implicit m: AdditiveGroup[T]): Group[T] = + new FromAlgebraGroup(m.additive) +} +private[algebird] trait FromAlgebraGroupImplicit0 extends FromAlgebraGroupImplicit1 { + implicit def fromAlgebraGroup[T](implicit m: AGroup[T]): Group[T] = new FromAlgebraGroup(m) +} + +object Group extends GeneratedGroupImplicits with ProductGroups with FromAlgebraGroupImplicit0 { // This pattern is really useful for typeclasses def negate[T](x: T)(implicit grp: Group[T]) = grp.negate(x) def minus[T](l: T, r: T)(implicit grp: Group[T]) = grp.minus(l, r) @@ -98,24 +116,26 @@ object Group extends GeneratedGroupImplicits with ProductGroups { Monoid.intTimes(i, v)(grp) } - implicit val nullGroup: Group[Null] = NullGroup - implicit val unitGroup: Group[Unit] = UnitGroup - implicit val boolGroup: Group[Boolean] = BooleanField - implicit val jboolGroup: Group[JBool] = JBoolField - implicit val intGroup: Group[Int] = IntRing - implicit val jintGroup: Group[JInt] = JIntRing - implicit val shortGroup: Group[Short] = ShortRing - implicit val jshortGroup: Group[JShort] = JShortRing - implicit val longGroup: Group[Long] = LongRing - implicit val bigIntGroup: Group[BigInt] = BigIntRing - implicit val bigDecimalGroup: Group[BigDecimal] = BigDecimalRing - implicit val jlongGroup: Group[JLong] = JLongRing - implicit val floatGroup: Group[Float] = FloatField - implicit val jfloatGroup: Group[JFloat] = JFloatField - implicit val doubleGroup: Group[Double] = DoubleField - implicit val jdoubleGroup: Group[JDouble] = JDoubleField + implicit def nullGroup: Group[Null] = NullGroup + implicit def unitGroup: Group[Unit] = UnitGroup + implicit def boolGroup: Group[Boolean] = BooleanRing + implicit def jboolGroup: Group[JBool] = JBoolRing + implicit def intGroup: Group[Int] = IntRing + implicit def jintGroup: Group[JInt] = JIntRing + implicit def shortGroup: Group[Short] = ShortRing + implicit def jshortGroup: Group[JShort] = JShortRing + implicit def longGroup: Group[Long] = LongRing + implicit def bigIntGroup: Group[BigInt] = BigIntRing + implicit def bigDecimalGroup: Group[BigDecimal] = implicitly[Ring[BigDecimal]] + implicit def jlongGroup: Group[JLong] = JLongRing + implicit def floatGroup: Group[Float] = FloatRing + implicit def jfloatGroup: Group[JFloat] = JFloatRing + implicit def doubleGroup: Group[Double] = DoubleRing + implicit def jdoubleGroup: Group[JDouble] = JDoubleRing implicit def optionGroup[T: Group] = new OptionGroup[T] implicit def indexedSeqGroup[T: Group]: Group[IndexedSeq[T]] = new IndexedSeqGroup[T] - implicit def mapGroup[K, V](implicit group: Group[V]) = new MapGroup[K, V]()(group) - implicit def scMapGroup[K, V](implicit group: Group[V]) = new ScMapGroup[K, V]()(group) + implicit def mapGroup[K, V](implicit group: Group[V]): Group[Map[K, V]] = + new MapGroup[K, V]()(group) + implicit def scMapGroup[K, V](implicit group: Group[V]): Group[scala.collection.Map[K, V]] = + new ScMapGroup[K, V]()(group) } diff --git a/algebird-core/src/main/scala/com/twitter/algebird/JavaMonoids.scala b/algebird-core/src/main/scala/com/twitter/algebird/JavaMonoids.scala index 280d40b53..a054039b2 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/JavaMonoids.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/JavaMonoids.scala @@ -47,34 +47,31 @@ object JLongRing extends Ring[JLong] { override def times(x: JLong, y: JLong) = x * y } -object JFloatField extends Field[JFloat] { +object JFloatRing extends Ring[JFloat] { override val zero = JFloat.valueOf(0.0f) override val one = JFloat.valueOf(1.0f) override def plus(x: JFloat, y: JFloat) = x + y override def negate(x: JFloat): JFloat = -x override def minus(x: JFloat, y: JFloat) = x - y override def times(x: JFloat, y: JFloat) = x * y - override def div(x: JFloat, y: JFloat) = { assertNotZero(y); x / y } } -object JDoubleField extends Field[JDouble] { +object JDoubleRing extends Ring[JDouble] { override val zero = JDouble.valueOf(0.0) override val one = JDouble.valueOf(1.0) override def plus(x: JDouble, y: JDouble) = x + y override def negate(x: JDouble): JDouble = -x override def minus(x: JDouble, y: JDouble) = x - y override def times(x: JDouble, y: JDouble) = x * y - override def div(x: JDouble, y: JDouble) = { assertNotZero(y); x / y } } -object JBoolField extends Field[JBool] { +object JBoolRing extends Ring[JBool] { override val zero = JBool.FALSE override val one = JBool.TRUE override def plus(x: JBool, y: JBool) = JBool.valueOf(x.booleanValue ^ y.booleanValue) override def negate(x: JBool) = x override def minus(x: JBool, y: JBool) = plus(x, y) override def times(x: JBool, y: JBool) = JBool.valueOf(x.booleanValue & y.booleanValue) - override def div(x: JBool, y: JBool) = { assertNotZero(y); x } } /** diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala index 9072ea2cb..50a362074 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala @@ -15,6 +15,8 @@ limitations under the License. */ package com.twitter.algebird +import algebra.{ Monoid => AMonoid } +import algebra.ring.{ AdditiveMonoid } import scala.annotation.implicitNotFound import scala.math.Equiv import scala.reflect.ClassTag @@ -30,9 +32,7 @@ import scala.collection.{ Map => ScMap } * 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] { - /** Returns the identity element of `$T` for [[plus]]. */ - def zero: T //additive identity +trait Monoid[@specialized(Int, Long, Float, Double) T] extends Semigroup[T] with AMonoid[T] with AdditiveMonoid[T] { def isNonZero(v: T): Boolean = (v != zero) def assertNotZero(v: T) { if (!isNonZero(v)) { @@ -47,8 +47,14 @@ trait Monoid[@specialized(Int, Long, Float, Double) T] extends Semigroup[T] { None } } - // Override this if there is a more efficient means to implement this - def sum(vs: TraversableOnce[T]): T = sumOption(vs).getOrElse(zero) + override def sum(vs: TraversableOnce[T]): T = sumOption(vs).getOrElse(zero) + + /** + * These are from algebra.Monoid + */ + override def additive: AMonoid[T] = this + override def empty: T = zero + override def combineAll(t: TraversableOnce[T]): T = sum(t) } // For Java interop so they get the default methods @@ -222,7 +228,21 @@ object AndValMonoid extends Monoid[AndVal] { else Some(AndVal(its.forall(_.get))) } -object Monoid extends GeneratedMonoidImplicits with ProductMonoids { +class FromAlgebraMonoid[T](m: AMonoid[T]) extends FromAlgebraSemigroup(m) with Monoid[T] { + override def sum(ts: TraversableOnce[T]): T = m.combineAll(ts) + override def zero: T = m.empty +} + +private[algebird] trait FromAlgebraMonoidImplicit1 { + implicit def fromAlgebraAdditiveMonoid[T](implicit m: AdditiveMonoid[T]): Monoid[T] = + new FromAlgebraMonoid(m.additive) +} + +private[algebird] trait FromAlgebraMonoidImplicit0 extends FromAlgebraMonoidImplicit1 { + implicit def fromAlgebraMonoid[T](implicit m: AMonoid[T]): Monoid[T] = new FromAlgebraMonoid(m) +} + +object Monoid extends GeneratedMonoidImplicits with ProductMonoids with FromAlgebraMonoidImplicit0 { // 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 @@ -261,23 +281,23 @@ object Monoid extends GeneratedMonoidImplicits with ProductMonoids { } } - 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 bigDecimalMonoid: Monoid[BigDecimal] = BigDecimalRing - 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 nullMonoid: Monoid[Null] = NullGroup + implicit def unitMonoid: Monoid[Unit] = UnitGroup + implicit def boolMonoid: Monoid[Boolean] = BooleanRing + implicit def jboolMonoid: Monoid[JBool] = JBoolRing + implicit def intMonoid: Monoid[Int] = IntRing + implicit def jintMonoid: Monoid[JInt] = JIntRing + implicit def shortMonoid: Monoid[Short] = ShortRing + implicit def jshortMonoid: Monoid[JShort] = JShortRing + implicit def bigIntMonoid: Monoid[BigInt] = BigIntRing + implicit def bigDecimalMonoid: Monoid[BigDecimal] = implicitly[Ring[BigDecimal]] + implicit def longMonoid: Monoid[Long] = LongRing + implicit def jlongMonoid: Monoid[JLong] = JLongRing + implicit def floatMonoid: Monoid[Float] = FloatRing + implicit def jfloatMonoid: Monoid[JFloat] = JFloatRing + implicit def doubleMonoid: Monoid[Double] = DoubleRing + implicit def jdoubleMonoid: Monoid[JDouble] = JDoubleRing + implicit def 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] diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Operators.scala b/algebird-core/src/main/scala/com/twitter/algebird/Operators.scala index e0184c7ad..57985ca48 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/Operators.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Operators.scala @@ -19,7 +19,6 @@ object Operators { implicit def toPlus[T: Semigroup](t: T) = new PlusOp(t) implicit def toMinus[T: Group](t: T) = new MinusOp(t) implicit def toTimes[T: Ring](t: T) = new TimesOp(t) - implicit def toDiv[T: Field](t: T) = new DivOp(t) implicit def toRichTraversableFromIterator[T](t: Iterator[T]): RichTraversable[T] = new RichTraversable(t) implicit def toRichTraversable[T](t: Traversable[T]): RichTraversable[T] = @@ -38,10 +37,6 @@ class TimesOp[T: Ring](t: T) { def *(other: T) = implicitly[Ring[T]].times(t, other) } -class DivOp[T: Field](t: T) { - def /(other: T) = implicitly[Field[T]].div(t, other) -} - class RichTraversable[T](t: TraversableOnce[T]) { def sumByKey[K, V](implicit ev: <:<[T, (K, V)], sg: Semigroup[V]): Map[K, V] = MapAlgebra.sumByKey(t.asInstanceOf[TraversableOnce[(K, V)]]) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala index 4657211de..d7cf9a4f4 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala @@ -16,6 +16,8 @@ limitations under the License. package com.twitter.algebird import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } +import algebra.ring.{ Ring => ARing } +import algebra.CommutativeGroup import scala.annotation.implicitNotFound /** @@ -42,11 +44,12 @@ import scala.annotation.implicitNotFound */ @implicitNotFound(msg = "Cannot find Ring type class for ${T}") -trait Ring[@specialized(Int, Long, Float, Double) T] extends Group[T] { - def one: T // Multiplicative identity - def times(l: T, r: T): T - // Left product: (((a * b) * c) * d) - def product(iter: TraversableOnce[T]): T = Ring.product(iter)(this) +trait Ring[@specialized(Int, Long, Float, Double) T] extends Group[T] with CommutativeGroup[T] with ARing[T] { + def one: T + def times(a: T, b: T): T + override def product(iter: TraversableOnce[T]): T = + if (iter.isEmpty) one // avoid hitting one as some have abused Ring for Rng + else iter.reduce(times) } // For Java interop so they get the default methods @@ -112,41 +115,95 @@ object LongRing extends Ring[Long] { else Some(sum(t)) } +object FloatRing extends Ring[Float] { + override def one = 1.0f + override def zero = 0.0f + override def negate(v: Float) = -v + override def plus(l: Float, r: Float) = l + r + override def minus(l: Float, r: Float) = l - r + override def times(l: Float, r: Float) = l * r +} + +object DoubleRing extends Ring[Double] { + override def one = 1.0 + override def zero = 0.0 + override def negate(v: Double) = -v + override def plus(l: Double, r: Double) = l + r + override def minus(l: Double, r: Double) = l - r + override def times(l: Double, r: Double) = l * r +} + +object BooleanRing extends Ring[Boolean] { + override def one = true + override def zero = false + override def negate(v: Boolean) = v + override def plus(l: Boolean, r: Boolean) = l ^ r + override def minus(l: Boolean, r: Boolean) = l ^ r + override def times(l: Boolean, r: Boolean) = l && r +} + object BigIntRing extends NumericRing[BigInt] object BigDecimalRing extends NumericRing[BigDecimal] -object Ring extends GeneratedRingImplicits with ProductRings { +trait NumericRingProvider { + implicit def numericRing[T: Numeric]: Ring[T] = new NumericRing[T] +} + +class FromAlgebraRing[T](r: ARing[T]) extends Ring[T] { + override def zero: T = r.zero + override def one: T = r.one + override def plus(a: T, b: T): T = r.plus(a, b) + override def negate(t: T): T = r.negate(t) + override def minus(a: T, b: T): T = r.minus(a, b) + override def sum(ts: TraversableOnce[T]): T = r.sum(ts) + override def sumOption(ts: TraversableOnce[T]): Option[T] = r.trySum(ts) + override def times(a: T, b: T): T = r.times(a, b) + override def product(ts: TraversableOnce[T]): T = r.product(ts) +} + +private[algebird] trait RingImplicits0 extends NumericRingProvider { + implicit def fromAlgebraRing[T](implicit r: ARing[T]): Ring[T] = + new FromAlgebraRing(r) +} + +object Ring extends GeneratedRingImplicits with ProductRings with RingImplicits0 { // This pattern is really useful for typeclasses def one[T](implicit rng: Ring[T]) = rng.one def times[T](l: T, r: T)(implicit rng: Ring[T]) = rng.times(l, r) - def asTimesMonoid[T](implicit ring: Ring[T]): Monoid[T] = - Monoid.from[T](ring.one)(ring.times _) - // Left product: (((a * b) * c) * d) - def product[T](iter: TraversableOnce[T])(implicit ring: Ring[T]) = { - // avoid touching one unless we need to (some items are pseudo-rings) - if (iter.isEmpty) ring.one - else iter.reduceLeft(ring.times _) + def asTimesMonoid[T](implicit ring: Ring[T]): Monoid[T] = new Monoid[T] { + def zero = ring.one + def plus(a: T, b: T): T = ring.times(a, b) + override def sumOption(ts: TraversableOnce[T]): Option[T] = + if (ts.isEmpty) None + else Some(ring.product(ts)) + override def sum(ts: TraversableOnce[T]): T = + ring.product(ts) } + // Left product: (((a * b) * c) * d) + def product[T](iter: TraversableOnce[T])(implicit ring: Ring[T]) = + ring.product(iter) + // If the ring doesn't have a one, or you want to distinguish empty cases: def productOption[T](it: TraversableOnce[T])(implicit rng: Ring[T]): Option[T] = - it.reduceLeftOption(rng.times _) + if (it.isEmpty) None + else Some(rng.product(it)) - implicit def numericRing[T: Numeric]: Ring[T] = new NumericRing[T] - implicit val boolRing: Ring[Boolean] = BooleanField - implicit val jboolRing: Ring[JBool] = JBoolField - implicit val intRing: Ring[Int] = IntRing - implicit val jintRing: Ring[JInt] = JIntRing - implicit val shortRing: Ring[Short] = ShortRing - implicit val jshortRing: Ring[JShort] = JShortRing - implicit val longRing: Ring[Long] = LongRing - implicit val bigIntRing: Ring[BigInt] = BigIntRing - implicit val bigDecimalRing: Ring[BigDecimal] = BigDecimalRing - implicit val jlongRing: Ring[JLong] = JLongRing - implicit val floatRing: Ring[Float] = FloatField - implicit val jfloatRing: Ring[JFloat] = JFloatField - implicit val doubleRing: Ring[Double] = DoubleField - implicit val jdoubleRing: Ring[JDouble] = JDoubleField + implicit def boolRing: Ring[Boolean] = BooleanRing + implicit def jboolRing: Ring[JBool] = JBoolRing + implicit def intRing: Ring[Int] = IntRing + implicit def jintRing: Ring[JInt] = JIntRing + implicit def shortRing: Ring[Short] = ShortRing + implicit def jshortRing: Ring[JShort] = JShortRing + implicit def longRing: Ring[Long] = LongRing + implicit def bigIntRing: Ring[BigInt] = BigIntRing + implicit def jlongRing: Ring[JLong] = JLongRing + implicit def floatRing: Ring[Float] = FloatRing + implicit def jfloatRing: Ring[JFloat] = JFloatRing + implicit def doubleRing: Ring[Double] = DoubleRing + implicit def jdoubleRing: Ring[JDouble] = JDoubleRing implicit def indexedSeqRing[T: Ring]: Ring[IndexedSeq[T]] = new IndexedSeqRing[T] - implicit def mapRing[K, V](implicit ring: Ring[V]) = new MapRing[K, V]()(ring) - implicit def scMapRing[K, V](implicit ring: Ring[V]) = new ScMapRing[K, V]()(ring) + implicit def mapRing[K, V](implicit ring: Ring[V]): Ring[Map[K, V]] = + new MapRing[K, V]()(ring) + implicit def scMapRing[K, V](implicit ring: Ring[V]): Ring[scala.collection.Map[K, V]] = + new ScMapRing[K, V]()(ring) } diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala b/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala index 0b4f53985..58c28e496 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala @@ -15,6 +15,8 @@ limitations under the License. */ package com.twitter.algebird +import algebra.{ Semigroup => ASemigroup } +import algebra.ring.{ AdditiveSemigroup } import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } import java.util.{ List => JList, Map => JMap } @@ -38,14 +40,7 @@ import macros.caseclass._ * @define T T */ @implicitNotFound(msg = "Cannot find Semigroup type class for ${T}") -trait Semigroup[@specialized(Int, Long, Float, Double) T] extends java.io.Serializable { - /** - * Combines two `$T` instances associatively. - * - * @return result of combining `l` and `r` - */ - def plus(l: T, r: T): T - +trait Semigroup[@specialized(Int, Long, Float, Double) T] extends ASemigroup[T] with AdditiveSemigroup[T] { /** * Returns an instance of `$T` calculated by summing all instances in * `iter` in one pass. Returns `None` if `iter` is empty, else @@ -58,6 +53,15 @@ trait Semigroup[@specialized(Int, Long, Float, Double) T] extends java.io.Serial */ def sumOption(iter: TraversableOnce[T]): Option[T] = iter.reduceLeftOption { plus(_, _) } + + /* + * These are methods from algebra + */ + override def trySum(iter: TraversableOnce[T]): Option[T] = sumOption(iter) + + override def additive: ASemigroup[T] = this + override def combine(l: T, r: T): T = plus(l, r) + override def combineAllOption(iter: TraversableOnce[T]): Option[T] = sumOption(iter) } // For Java interop so they get the default sumOption @@ -94,7 +98,25 @@ class EitherSemigroup[L, R](implicit semigroupl: Semigroup[L], semigroupr: Semig } } -object Semigroup extends GeneratedSemigroupImplicits with ProductSemigroups { +class FromAlgebraSemigroup[T](sg: ASemigroup[T]) extends Semigroup[T] { + override def plus(l: T, r: T): T = sg.combine(l, r) + override def sumOption(ts: TraversableOnce[T]): Option[T] = sg.combineAllOption(ts) +} + +/** + * An Algebra semigroup can be an Algebird semigroup + */ +private[algebird] trait FromAlgebraSemigroupImplicit1 { + implicit def fromAlgebraAdditiveSemigroup[T](implicit sg: AdditiveSemigroup[T]): Semigroup[T] = + new FromAlgebraSemigroup(sg.additive) +} + +private[algebird] trait FromAlgebraSemigroupImplicit0 extends FromAlgebraSemigroupImplicit1 { + implicit def fromAlgebraSemigroup[T](implicit sg: ASemigroup[T]): Semigroup[T] = + new FromAlgebraSemigroup(sg) +} + +object Semigroup extends GeneratedSemigroupImplicits with ProductSemigroups with FromAlgebraSemigroupImplicit0 { def maybePlus[T](opt: Option[T], t: T)(implicit sg: Semigroup[T]): T = opt match { case None => t @@ -111,7 +133,8 @@ object Semigroup extends GeneratedSemigroupImplicits with ProductSemigroups { def sumOption[T](iter: TraversableOnce[T])(implicit sg: Semigroup[T]): Option[T] = sg.sumOption(iter) - def from[T](associativeFn: (T, T) => T): Semigroup[T] = new Semigroup[T] { def plus(l: T, r: T) = associativeFn(l, r) } + def from[T](associativeFn: (T, T) => T): Semigroup[T] = + new Semigroup[T] { def plus(l: T, r: T) = associativeFn(l, r) } /** * Same as v + v + v .. + v (i times in total) @@ -162,23 +185,22 @@ object Semigroup extends GeneratedSemigroupImplicits with ProductSemigroups { } } - implicit val nullSemigroup: Semigroup[Null] = NullGroup - implicit val unitSemigroup: Semigroup[Unit] = UnitGroup - implicit val boolSemigroup: Semigroup[Boolean] = BooleanField - implicit val jboolSemigroup: Semigroup[JBool] = JBoolField - implicit val intSemigroup: Semigroup[Int] = IntRing - implicit val jintSemigroup: Semigroup[JInt] = JIntRing - implicit val shortSemigroup: Semigroup[Short] = ShortRing - implicit val jshortSemigroup: Semigroup[JShort] = JShortRing - implicit val longSemigroup: Semigroup[Long] = LongRing - implicit val bigIntSemigroup: Semigroup[BigInt] = BigIntRing - implicit val bigDecimalSemigroup: Semigroup[BigDecimal] = BigDecimalRing - implicit val jlongSemigroup: Semigroup[JLong] = JLongRing - implicit val floatSemigroup: Semigroup[Float] = FloatField - implicit val jfloatSemigroup: Semigroup[JFloat] = JFloatField - implicit val doubleSemigroup: Semigroup[Double] = DoubleField - implicit val jdoubleSemigroup: Semigroup[JDouble] = JDoubleField - implicit val stringSemigroup: Semigroup[String] = StringMonoid + implicit def nullSemigroup: Semigroup[Null] = NullGroup + implicit def unitSemigroup: Semigroup[Unit] = UnitGroup + implicit def boolSemigroup: Semigroup[Boolean] = BooleanRing + implicit def jboolSemigroup: Semigroup[JBool] = JBoolRing + implicit def intSemigroup: Semigroup[Int] = IntRing + implicit def jintSemigroup: Semigroup[JInt] = JIntRing + implicit def shortSemigroup: Semigroup[Short] = ShortRing + implicit def jshortSemigroup: Semigroup[JShort] = JShortRing + implicit def longSemigroup: Semigroup[Long] = LongRing + implicit def bigIntSemigroup: Semigroup[BigInt] = BigIntRing + implicit def jlongSemigroup: Semigroup[JLong] = JLongRing + implicit def floatSemigroup: Semigroup[Float] = FloatRing + implicit def jfloatSemigroup: Semigroup[JFloat] = JFloatRing + implicit def doubleSemigroup: Semigroup[Double] = DoubleRing + implicit def jdoubleSemigroup: Semigroup[JDouble] = JDoubleRing + implicit def stringSemigroup: Semigroup[String] = StringMonoid implicit def optionSemigroup[T: Semigroup]: Semigroup[Option[T]] = new OptionMonoid[T] implicit def listSemigroup[T]: Semigroup[List[T]] = new ListMonoid[T] implicit def seqSemigroup[T]: Semigroup[Seq[T]] = new SeqMonoid[T] diff --git a/algebird-core/src/main/scala/com/twitter/algebird/VectorSpace.scala b/algebird-core/src/main/scala/com/twitter/algebird/VectorSpace.scala index 94deae3c1..f997facff 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/VectorSpace.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/VectorSpace.scala @@ -27,25 +27,26 @@ import scala.annotation.implicitNotFound */ object VectorSpace { def scale[F, C[_]](v: F, c: C[F])(implicit vs: VectorSpace[F, C]): C[F] = vs.scale(v, c) - def from[F, C[_]](scaleFn: (F, C[F]) => C[F])(implicit fField: Field[F], cGroup: Group[C[F]]) = new VectorSpace[F, C] { - def field = fField + def from[F, C[_]](scaleFn: (F, C[F]) => C[F])(implicit r: Ring[F], cGroup: Group[C[F]]) = new VectorSpace[F, C] { + def ring = r def group = cGroup - def scale(v: F, c: C[F]) = if (field.isNonZero(v)) scaleFn(v, c) else cGroup.zero + def scale(v: F, c: C[F]) = if (r.isNonZero(v)) scaleFn(v, c) else cGroup.zero } // Implicits - implicit def indexedSeqSpace[T: Field] = + implicit def indexedSeqSpace[T: Ring] = from[T, IndexedSeq]{ (s, seq) => seq.map(Ring.times(s, _)) } - implicit def mapSpace[K, T: Field] = + implicit def mapSpace[K, T: Ring] = from[T, ({ type x[a] = Map[K, a] })#x] { (s, m) => m.mapValues(Ring.times(s, _)) } // TODO: add implicits for java lists, arrays, and options } -@implicitNotFound(msg = "Cannot find VectorSpace type class for Container: ${C} and Field: ${F}") +@implicitNotFound(msg = "Cannot find VectorSpace type class for Container: ${C} and Ring: ${F}") trait VectorSpace[F, C[_]] extends java.io.Serializable { - implicit def field: Field[F] + implicit def ring: Ring[F] + def field: Ring[F] = ring // this is for compatibility with older versions implicit def group: Group[C[F]] def scale(v: F, c: C[F]): C[F] } diff --git a/algebird-core/src/main/scala/com/twitter/algebird/field.scala b/algebird-core/src/main/scala/com/twitter/algebird/field.scala new file mode 100644 index 000000000..29297c36b --- /dev/null +++ b/algebird-core/src/main/scala/com/twitter/algebird/field.scala @@ -0,0 +1,97 @@ +package com.twitter.algebird + +import java.lang.{ Float => JFloat, Double => JDouble } +/** + * This is here to ease transition to using algebra.Field as the field + * type. Intended use is to do: + * + * {code} import com.twitter.algebird.field._ {/code} + * + * Note, this are not strictly lawful since floating point + * arithmetic using IEEE-754 is only approximately associative + * and distributive. + */ +object field { + implicit object ForFloat extends Field[Float] { + override def one: Float = 1.0f + override def zero: Float = 0.0f + override def negate(v: Float): Float = -v + override def plus(l: Float, r: Float): Float = l + r + override def sum(v: TraversableOnce[Float]): Float = { + var sum = 0.0f + v.foreach { sum += _ } + return sum + } + override def trySum(v: TraversableOnce[Float]): Option[Float] = + if (v.isEmpty) None else Some(sum(v)) + override def minus(l: Float, r: Float): Float = l - r + override def times(l: Float, r: Float): Float = l * r + override def div(l: Float, r: Float): Float = l / r + } + implicit object ForJFloat extends Field[JFloat] { + override val one: JFloat = JFloat.valueOf(1.0f) + override val zero: JFloat = JFloat.valueOf(0.0f) + override def negate(v: JFloat): JFloat = -v + override def plus(l: JFloat, r: JFloat): JFloat = l + r + override def sum(v: TraversableOnce[JFloat]): JFloat = { + var sum = 0.0f + v.foreach { sum += _ } + return sum + } + override def trySum(v: TraversableOnce[JFloat]): Option[JFloat] = + if (v.isEmpty) None else Some(sum(v)) + override def minus(l: JFloat, r: JFloat): JFloat = l - r + override def times(l: JFloat, r: JFloat): JFloat = l * r + override def div(l: JFloat, r: JFloat): JFloat = l / r + } + implicit object ForDouble extends Field[Double] { + override def one: Double = 1.0 + override def zero: Double = 0.0 + override def negate(v: Double): Double = -v + override def plus(l: Double, r: Double): Double = l + r + override def sum(v: TraversableOnce[Double]): Double = { + var sum = 0.0 + v.foreach { sum += _.floatValue } + return sum + } + override def trySum(v: TraversableOnce[Double]): Option[Double] = + if (v.isEmpty) None else Some(sum(v)) + override def minus(l: Double, r: Double): Double = l - r + override def times(l: Double, r: Double): Double = l * r + override def div(l: Double, r: Double): Double = l / r + } + implicit object ForJDouble extends Field[JDouble] { + override val one: JDouble = JDouble.valueOf(1.0) + override val zero: JDouble = JDouble.valueOf(0.0) + override def negate(v: JDouble): JDouble = -v + override def plus(l: JDouble, r: JDouble): JDouble = l + r + override def sum(v: TraversableOnce[JDouble]): JDouble = { + var sum = 0.0 + v.foreach { sum += _.doubleValue } + return sum + } + override def trySum(v: TraversableOnce[JDouble]): Option[JDouble] = + if (v.isEmpty) None else Some(sum(v)) + override def minus(l: JDouble, r: JDouble): JDouble = l - r + override def times(l: JDouble, r: JDouble): JDouble = l * r + override def div(l: JDouble, r: JDouble): JDouble = l / r + } + + /** + * These methods were originally on algebird.Field, but are not present on + * algebra.Field + */ + implicit class AlgebirdFieldEnrichments[T](val field: Field[T]) extends AnyVal { + def assertNotZero(t: T): Unit = + if (t == field.zero) throw new IllegalArgumentException(s"found $t, require ${field.zero}") + else () + + def nonZeroOption(t: T): Option[T] = + if (t == field.zero) None + else Some(t) + + def isNonZero(v: T): Boolean = v != field.zero + + def inverse(t: T): T = field.reciprocal(t) + } +} diff --git a/algebird-core/src/main/scala/com/twitter/algebird/package.scala b/algebird-core/src/main/scala/com/twitter/algebird/package.scala index 24bbf7d42..56771318a 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/package.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/package.scala @@ -24,4 +24,10 @@ package object algebird { */ private[algebird] abstract class AbstractIterable[T] extends Iterable[T] private[algebird] abstract class AbstractIterator[T] extends Iterator[T] + + /** + * To keep code using algebird.Field compiling, we export algebra Field + */ + type Field[V] = algebra.ring.Field[V] + val Field = algebra.ring.Field } diff --git a/algebird-core/src/test/scala/com/twitter/algebird/AlgebraResolutionTest.scala b/algebird-core/src/test/scala/com/twitter/algebird/AlgebraResolutionTest.scala new file mode 100644 index 000000000..2783f5f14 --- /dev/null +++ b/algebird-core/src/test/scala/com/twitter/algebird/AlgebraResolutionTest.scala @@ -0,0 +1,46 @@ +package com.twitter.algebird + +import org.scalatest.FunSuite + +/** + * This is just a compilation test that we can resolve + * algebird types from implicit algebra instances. + */ +class AlgebraResolutionTest extends FunSuite { + // A type with no built in algebird algebras + trait Empty { } + test("algebra.Semigroup") { + implicit def fakeSemigroup[T]: algebra.Semigroup[T] = null + implicitly[Semigroup[Empty]] + } + test("algebra.ring.AdditiveSemigroup") { + implicit def fakeAdditiveSemigroup[T]: algebra.ring.AdditiveSemigroup[T] = + Semigroup.from[T] { (a, b) => a } + + implicitly[Semigroup[Empty]] + } + test("algebra.Monoid") { + implicit def fakeMonoid[T]: algebra.Monoid[T] = null + implicitly[Monoid[Empty]] + } + test("algebra.ring.AdditiveMonoid") { + implicit def fakeAdditiveMonoid[T]: algebra.ring.AdditiveMonoid[T] = + Monoid.from[T](null.asInstanceOf[T]) { (a, b) => a } + + implicitly[Monoid[Empty]] + } + test("algebra.Group") { + implicit def fakeGroup[T]: algebra.Group[T] = null + implicitly[Group[Empty]] + } + test("algebra.ring.AdditiveGroup") { + implicit def fakeAdditiveGroup[T]: algebra.ring.AdditiveGroup[T] = + implicitly[Group[Null]].asInstanceOf[Group[T]] + + implicitly[Group[Empty]] + } + test("algebra.ring.Ring") { + implicit def fakeRing[T]: algebra.ring.Ring[T] = null + implicitly[Ring[Empty]] + } +} diff --git a/algebird-test/src/main/scala/com/twitter/algebird/BaseProperties.scala b/algebird-test/src/main/scala/com/twitter/algebird/BaseProperties.scala index cd7a11855..f31c22ec9 100644 --- a/algebird-test/src/main/scala/com/twitter/algebird/BaseProperties.scala +++ b/algebird-test/src/main/scala/com/twitter/algebird/BaseProperties.scala @@ -316,15 +316,4 @@ object BaseProperties extends MetricProperties { def ringLawsEquiv[T: Ring: Arbitrary: Equiv]: Prop = validOneEquiv[T] && pseudoRingLawsEquiv[T] - - def hasMultiplicativeInverse[T: Field: Arbitrary]: Prop = - 'hasMultiplicativeInverse |: forAll { (a: T) => - val fld = implicitly[Field[T]] - (!fld.isNonZero(a)) || { - val inva = fld.inverse(a) - (fld.times(inva, a) == fld.one) && (fld.times(a, inva) == fld.one) - } - } - - def fieldLaws[T: Field: Arbitrary]: Prop = ringLaws[T] && hasMultiplicativeInverse[T] } diff --git a/algebird-test/src/test/scala/com/twitter/algebird/AdJoinedUnitRingLaws.scala b/algebird-test/src/test/scala/com/twitter/algebird/AdJoinedUnitRingLaws.scala index d4ce58736..2f77c1389 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/AdJoinedUnitRingLaws.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/AdJoinedUnitRingLaws.scala @@ -16,6 +16,7 @@ limitations under the License. package com.twitter.algebird +import algebra.ring.Rng import com.twitter.algebird.BaseProperties._ import com.twitter.algebird.scalacheck.arbitrary._ import org.scalacheck.Prop.forAll @@ -28,6 +29,8 @@ class AdjoinedUnitRingLaws extends CheckProperties { } } + implicit def rng[T: Ring]: Rng[T] = implicitly[Ring[T]] + property("AdjoinedUnit[Int] is a Ring") { ringLaws[AdjoinedUnit[Int]] } property("AdjoinedUnit[Long] is a Ring") { ringLaws[AdjoinedUnit[Long]] } } diff --git a/algebird-test/src/test/scala/com/twitter/algebird/ApplicativeProperties.scala b/algebird-test/src/test/scala/com/twitter/algebird/ApplicativeProperties.scala index 0704913ec..88caa6bc9 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/ApplicativeProperties.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/ApplicativeProperties.scala @@ -88,9 +88,4 @@ class ApplicativeProperties extends CheckProperties { implicit val optSg = new ApplicativeRing[Int, Some] ringLaws[Some[Int]] } - - property("Applicative Field") { - implicit val optSg = new ApplicativeField[Boolean, Some] - fieldLaws[Some[Boolean]] - } } diff --git a/algebird-test/src/test/scala/com/twitter/algebird/JavaBoxedTests.scala b/algebird-test/src/test/scala/com/twitter/algebird/JavaBoxedTests.scala index 5d53d2d12..67a8cec83 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/JavaBoxedTests.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/JavaBoxedTests.scala @@ -25,14 +25,6 @@ class JavaBoxedTests extends CheckProperties { yield JLong.valueOf(v) } - property("Boolean is a Field") { - fieldLaws[Boolean] - } - - property("JBoolean is a Field") { - fieldLaws[JBool] - } - property("Int is a Ring") { ringLaws[Int] } diff --git a/algebird-test/src/test/scala/com/twitter/algebird/NumericSpecification.scala b/algebird-test/src/test/scala/com/twitter/algebird/NumericSpecification.scala index 022545dfe..5dd90dda4 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/NumericSpecification.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/NumericSpecification.scala @@ -131,18 +131,4 @@ class NumericSpecification extends PropSpec with PropertyChecks with Matchers { property("Float times") { timesNumericProp[Float] } - - property("Double div") { - forAll { (a: Double, b: Double) => - val fld = implicitly[Field[Double]] - assert((!fld.isNonZero(b)) || ((a / b) == fld.div(a, b))) - } - } - - property("Float div") { - forAll { (a: Float, b: Float) => - val fld = implicitly[Field[Float]] - assert((!fld.isNonZero(b)) || ((a / b) == fld.div(a, b))) - } - } } diff --git a/algebird-test/src/test/scala/com/twitter/algebird/OperatorTest.scala b/algebird-test/src/test/scala/com/twitter/algebird/OperatorTest.scala index 1e268cc71..8e8eca46c 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/OperatorTest.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/OperatorTest.scala @@ -17,8 +17,5 @@ class OperatorTest extends WordSpec with Matchers { assert(Map(1 -> 3) * Map(2 -> 4) == Map[Int, Int]()) assert(Map(1 -> 3) * Map(1 -> 4) == Map(1 -> 12)) } - "allow /" in { - assert(true / true == true) - } } } diff --git a/algebird-util/src/main/scala/com/twitter/algebird/util/UtilAlgebras.scala b/algebird-util/src/main/scala/com/twitter/algebird/util/UtilAlgebras.scala index 2e09c46d4..3dfe3d766 100644 --- a/algebird-util/src/main/scala/com/twitter/algebird/util/UtilAlgebras.scala +++ b/algebird-util/src/main/scala/com/twitter/algebird/util/UtilAlgebras.scala @@ -53,22 +53,4 @@ object UtilAlgebras { implicit def trySemigroup[T: Semigroup]: Semigroup[Try[T]] = new ApplicativeSemigroup[T, Try] implicit def tryMonoid[T: Monoid]: Monoid[Try[T]] = new ApplicativeMonoid[T, Try] - - @deprecated("futureGroup is broken and will be removed at the next minor version bump", since = "0.12.3") - implicit def futureGroup[T: Group]: Group[Future[T]] = new ApplicativeGroup[T, Future] - - @deprecated("futureRing is broken and will be removed at the next minor version bump", since = "0.12.3") - implicit def futureRing[T: Ring]: Ring[Future[T]] = new ApplicativeRing[T, Future] - - @deprecated("futureField is broken and will be removed at the next minor version bump", since = "0.12.3") - implicit def futureField[T: Field]: Field[Future[T]] = new ApplicativeField[T, Future] - - @deprecated("tryGroup is broken and will be removed at the next minor version bump", since = "0.12.3") - implicit def tryGroup[T: Group]: Group[Try[T]] = new ApplicativeGroup[T, Try] - - @deprecated("tryRing is broken and will be removed at the next minor version bump", since = "0.12.3") - implicit def tryRing[T: Ring]: Ring[Try[T]] = new ApplicativeRing[T, Try] - - @deprecated("tryField is broken and will be removed at the next minor version bump", since = "0.12.3") - implicit def tryField[T: Field]: Field[Try[T]] = new ApplicativeField[T, Try] } diff --git a/build.sbt b/build.sbt index c1892204d..ed7e47b1e 100644 --- a/build.sbt +++ b/build.sbt @@ -8,9 +8,13 @@ import pl.project13.scala.sbt.JmhPlugin import sbtunidoc.Plugin.UnidocKeys._ import scalariform.formatter.preferences._ +val algebraVersion = "0.6.0" +val bijectionVersion = "0.9.0" +val javaEwahVersion = "0.6.6" val paradiseVersion = "2.1.0" val quasiquotesVersion = "2.1.0" -val bijectionVersion = "0.9.0" +val scalaTestVersion = "3.0.0" +val scalacheckVersion = "1.13.1" val utilVersion = "6.20.0" def scalaBinaryVersion(scalaVersion: String) = scalaVersion match { @@ -184,13 +188,14 @@ def module(name: String) = { } lazy val algebirdCore = module("core").settings( - test := { }, // All tests reside in algebirdTest initialCommands := """ import com.twitter.algebird._ """.stripMargin('|'), libraryDependencies <++= (scalaVersion) { scalaVersion => - Seq("com.googlecode.javaewah" % "JavaEWAH" % "0.6.6", - "org.scala-lang" % "scala-reflect" % scalaVersion) ++ { + Seq("com.googlecode.javaewah" % "JavaEWAH" % javaEwahVersion, + "org.typelevel" %% "algebra" % algebraVersion, + "org.scala-lang" % "scala-reflect" % scalaVersion, + "org.scalatest" %% "scalatest" % scalaTestVersion % "test") ++ { if (isScala210x(scalaVersion)) Seq("org.scalamacros" %% "quasiquotes" % quasiquotesVersion) else @@ -205,8 +210,8 @@ lazy val algebirdCore = module("core").settings( lazy val algebirdTest = module("test").settings( testOptions in Test ++= Seq(Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "4")), libraryDependencies <++= (scalaVersion) { scalaVersion => - Seq("org.scalacheck" %% "scalacheck" % "1.13.1", - "org.scalatest" %% "scalatest" % "3.0.0") ++ { + Seq("org.scalacheck" %% "scalacheck" % scalacheckVersion, + "org.scalatest" %% "scalatest" % scalaTestVersion) ++ { if (isScala210x(scalaVersion)) Seq("org.scalamacros" %% "quasiquotes" % quasiquotesVersion) else diff --git a/sbt b/sbt index 713c48653..49927dbcd 100755 --- a/sbt +++ b/sbt @@ -4,7 +4,7 @@ # Author: Paul Phillips # todo - make this dynamic -declare -r sbt_release_version="0.13.9" +declare -r sbt_release_version="0.13.11" declare -r sbt_unreleased_version="0.13.9-M1" declare -r buildProps="project/build.properties"