Skip to content

Commit

Permalink
Merge branch 'release/0.8.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
ianoc committed Sep 9, 2014
2 parents 2fc763c + 51b3b9a commit e3f3fcb
Show file tree
Hide file tree
Showing 81 changed files with 2,185 additions and 1,198 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
language: scala
scala:
- 2.9.3
- 2.10.4
- 2.11.2
11 changes: 11 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Algebird #

### Version 0.8.0 ###
* Removes deprecated monoid: https://github.com/twitter/algebird/pull/342
* Use some value classes: https://github.com/twitter/algebird/pull/340
* WIP: Algebird 210 211: https://github.com/twitter/algebird/pull/337
* Use Builder in Seq/List Monoids: https://github.com/twitter/algebird/pull/338
* Add a commment to close 334: https://github.com/twitter/algebird/pull/339
* Make trait public that should never have been private: https://github.com/twitter/algebird/pull/335
* Fix some issues with Successible/Predessible: https://github.com/twitter/algebird/pull/332
* Add Applicative and Functor as superclasses of Monad: https://github.com/twitter/algebird/pull/330
* Updated Maven section to updated version 0.7.0: https://github.com/twitter/algebird/pull/329

### Version 0.7.0 ###
* simplification for spacesaver. before buckets were kept in an option str...: https://github.com/twitter/algebird/pull/308
* Dynamic Summer, may use heuristics to decide not to keep a tuple in a buffer for aggregation.: https://github.com/twitter/algebird/pull/314
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ See the [current API documentation](http://twitter.github.com/algebird) for more
## What can you do with this code?

```scala
> ./sbt algebird-core/console

Welcome to Scala version 2.9.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_07).
Type in expressions to have them evaluated.
Type :help for more information.
Expand Down Expand Up @@ -43,7 +45,7 @@ Discussion occurs primarily on the [Algebird mailing list](https://groups.google

## Maven

Algebird modules are available on maven central. The current groupid and version for all modules is, respectively, `"com.twitter"` and `0.6.0`.
Algebird modules are available on maven central. The current groupid and version for all modules is, respectively, `"com.twitter"` and `0.8.0`.

Current published artifacts are

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class BijectedSemigroup[T, U](implicit val sg: Semigroup[T], bij: ImplicitBiject
def bijection: Bijection[U, T] = bij.bijection.inverse
override def plus(l: U, r: U): U = sg.plus(l.as[T], r.as[T]).as[U]
override def sumOption(iter: TraversableOnce[U]): Option[U] =
sg.sumOption(iter.map { _.as[T] }).as[Option[U]]
sg.sumOption(iter.map { _.as[T] }).map(_.as[U])
}

class BijectedMonoid[T, U](implicit val monoid: Monoid[T], bij: ImplicitBijection[T, U]) extends BijectedSemigroup[T, U] with Monoid[U] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

package com.twitter.algebird.bijection

import org.scalatest.{ PropSpec, Matchers }
import org.scalatest.prop.PropertyChecks
import org.scalacheck.{ Arbitrary, Properties }

object AlgebirdBijectionLaws extends Properties("AlgebirdBijections") {
class AlgebirdBijectionLaws extends PropSpec with PropertyChecks with Matchers {
// TODO: Fill in tests. Ideally we'd publish an algebird-testing
// module before merging this in.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
Copyright 2014 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

/**
* Simple implementation of an Applicative type-class.
* There are many choices for the canonical second operation (join, sequence, joinWith, ap),
* all equivalent. For a Functor modeling concurrent computations with failure, like Future,
* combining results with join can save a lot of time over combining with flatMap. (Given two
* operations, if the second fails before the first completes, one can fail the entire computation
* right then. With flatMap, one would have to wait for the first operation to complete before
* failing it.)
*
* Laws Applicatives must follow:
* map(apply(x))(f) == apply(f(x))
* join(apply(x), apply(y)) == apply((x, y))
* (sequence and joinWith specialize join - they should behave appropriately)
*/
@implicitNotFound(msg = "Cannot find Applicative type class for ${M}")
trait Applicative[M[_]] extends Functor[M] {
// in haskell, called return, but that's a reserved word
// constructs an Applicative instance from the given value, e.g. List(1)
def apply[T](v: T): M[T]
def join[T, U](mt: M[T], mu: M[U]): M[(T, U)]
def sequence[T](ms: Seq[M[T]]): M[Seq[T]] =
ms match {
case Seq() => apply(Seq.empty)
case Seq(m) => map(m) { Seq(_) }
case Seq(m, n) => joinWith(m, n) { Seq(_, _) }
case _ =>
val mb =
ms.foldLeft(apply(Seq.newBuilder[T])) { (mb, mt) =>
joinWith(mb, mt) { (b, t) => b += t }
}
map(mb) { _.result }
}
def joinWith[T, U, V](mt: M[T], mu: M[U])(fn: (T, U) => V): M[V] =
map(join(mt, mu)) { case (t, u) => fn(t, u) }
}

/**
* For use from Java/minimizing code bloat in scala
*/
abstract class AbstractApplicative[M[_]] extends Applicative[M]

/**
* Follows the type-class pattern for the Applicative trait
*/
object Applicative {
/** Get the Applicative for a type, e.g: Applicative[List] */
def apply[M[_]](implicit app: Applicative[M]): Applicative[M] = app
def join[M[_], T, U](mt: M[T], mu: M[U])(implicit app: Applicative[M]): M[(T, U)] =
app.join(mt, mu)
def sequence[M[_], T](ms: Seq[M[T]])(implicit app: Applicative[M]): M[Seq[T]] =
app.sequence(ms)
def joinWith[M[_], T, U, V](mt: M[T], mu: M[U])(fn: (T, U) => V)(implicit app: Applicative[M]): M[V] =
app.joinWith(mt, mu)(fn)

// Set up the syntax magic (allow .pure[Int] syntax and flatMap in for):
// import Applicative.{pureOp, operators} to get
implicit def pureOp[A](a: A) = new PureOp(a)
implicit def operators[A, M[_]](m: M[A])(implicit app: Applicative[M]) =
new ApplicativeOperators(m)(app)
}

// This is the enrichment pattern to allow syntax like: 1.pure[List] == List(1)
// if we put a pure method in Applicative, it would take two type parameters, only one
// of which could be inferred, and it's annoying to write Applicative.pure[Int,List](1)
class PureOp[A](a: A) {
def pure[M[_]](implicit app: Applicative[M]) = app(a)
}

/**
* This enrichment allows us to use our Applicative instances in for expressions:
* if (import Applicative._) has been done
*/
class ApplicativeOperators[A, M[_]](m: M[A])(implicit app: Applicative[M]) extends FunctorOperators[A, M](m) {
def join[B](mb: M[B]): M[(A, B)] = app.join(m, mb)
def joinWith[B, C](mb: M[B])(fn: (A, B) => C): M[C] = app.joinWith(m, mb)(fn)
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class RichCBitSet(val cb: CBitSet) {
def toBitSet(width: Int): BitSet = {
val a = new Array[Long]((width + 63) / 64)
cb.asScala.foreach{ i: java.lang.Integer => a(i.intValue / 64) |= 1L << (i.intValue % 64) }
BitSet.fromArray(a)
BitSet.fromBitMask(a)
}
}

Expand Down
56 changes: 56 additions & 0 deletions algebird-core/src/main/scala/com/twitter/algebird/Functor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
Copyright 2014 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

/**
* Simple implementation of a Functor type-class.
*
* Laws Functors must follow:
* map(m)(id) == m
* map(m)(f andThen g) == map(map(m)(f))(g)
*/
@implicitNotFound(msg = "Cannot find Functor type class for ${M}")
trait Functor[M[_]] {
def map[T, U](m: M[T])(fn: (T) => U): M[U]
}

/**
* For use from Java/minimizing code bloat in scala
*/
abstract class AbstractFunctor[M[_]] extends Functor[M]

/**
* Follows the type-class pattern for the Functor trait
*/
object Functor {
/** Get the Functor for a type, e.g: Functor[List] */
def apply[M[_]](implicit functor: Functor[M]): Functor[M] = functor
def map[M[_], T, U](m: M[T])(fn: (T) => U)(implicit functor: Functor[M]) = functor.map(m)(fn)

implicit def operators[A, M[_]](m: M[A])(implicit functor: Functor[M]) =
new FunctorOperators(m)(functor)
}

/**
* This enrichment allows us to use our Functor instances in for expressions:
* if (import Functor._) has been done
*/
class FunctorOperators[A, M[_]](m: M[A])(implicit functor: Functor[M]) {
// This is called fmap in haskell
def map[U](fn: (A) => U): M[U] = functor.map(m)(fn)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import java.nio._
* The bytes are assumed to be never modified. The only reason we did not use IndexedSeq[Byte] instead of Array[Byte] is
* because a ByteBuffer is used internally in MinHasher and it can wrap Array[Byte].
*/
case class MinHashSignature(bytes: Array[Byte])
case class MinHashSignature(bytes: Array[Byte]) extends AnyVal

object MinHasher {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ package com.twitter.algebird
*/

// This is basically a sigil class to represent using the Min-Plus semi-ring
sealed abstract class MinPlus[+V] extends java.io.Serializable
sealed trait MinPlus[+V] extends Any with java.io.Serializable
case object MinPlusZero extends MinPlus[Nothing]
case class MinPlusValue[V](get: V) extends MinPlus[V]
case class MinPlusValue[V](get: V) extends AnyVal with MinPlus[V]

class MinPlusSemiring[V](implicit monoid: Monoid[V], ord: Ordering[V]) extends Ring[MinPlus[V]] {
override def zero = MinPlusZero
override def negate(mv: MinPlus[V]) =
sys.error("MinPlus is a semi-ring, there is no additive inverse")
override lazy val one = MinPlusValue(monoid.zero)
override def one: MinPlus[V] = MinPlusValue(monoid.zero)
// a+b = min(a,b)
override def plus(left: MinPlus[V], right: MinPlus[V]) =
// We are doing the if to avoid an allocation:
Expand Down
42 changes: 19 additions & 23 deletions algebird-core/src/main/scala/com/twitter/algebird/Monad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,27 @@ import java.util.{ List => JList, Map => JMap }

import scala.annotation.implicitNotFound
import collection.GenTraversable

/**
* Simple implementation of a Monad type-class
* Simple implementation of a Monad type-class.
* Subclasses only need to override apply and flatMap, but they should override map,
* join, joinWith, and sequence if there are better implementations.
*
* Laws Monads must follow:
* identities:
* flatMap(apply(x))(fn) == fn(x)
* flatMap(m)(apply _) == m
* associativity on flatMap (you can either flatMap f first, or f to g:
* flatMap(flatMap(m)(f))(g) == flatMap(m) { x => flatMap(f(x))(g) }
*/
@implicitNotFound(msg = "Cannot find Monad type class for ${M}")
trait Monad[M[_]] {
// in haskell, called return, but that's a reserved word
// constructs a Monad instance from the given value, e.g. List(1)
def apply[T](v: T): M[T]
trait Monad[M[_]] extends Applicative[M] {
def flatMap[T, U](m: M[T])(fn: (T) => M[U]): M[U]
def map[T, U](m: M[T])(fn: (T) => U): M[U] = flatMap(m)((t: T) => apply(fn(t)))
// Laws these must follow are:
// identities:
// flatMap(apply(x))(fn) == fn(x)
// flatMap(m)(apply _) == m
// associativity on flatMap (you can either flatMap f first, or f to g:
// flatMap(flatMap(m)(f))(g) == flatMap(m) { x => flatMap(f(x))(g) }
override def map[T, U](m: M[T])(fn: (T) => U): M[U] = flatMap(m)((t: T) => apply(fn(t)))
override def join[T, U](mt: M[T], mu: M[U]): M[(T, U)] =
flatMap(mt) { (t: T) =>
map(mu) { (u: U) => (t, u) }
}
}

/**
Expand Down Expand Up @@ -90,24 +95,15 @@ object Monad {
// Set up the syntax magic (allow .pure[Int] syntax and flatMap in for):
// import Monad.{pureOp, operators} to get
implicit def pureOp[A](a: A) = new PureOp(a)
implicit def operators[A, M[A]](m: M[A])(implicit monad: Monad[M]) =
implicit def operators[A, M[_]](m: M[A])(implicit monad: Monad[M]) =
new MonadOperators(m)(monad)
}

// This is the enrichment pattern to allow syntax like: 1.pure[List] == List(1)
// if we put a pure method in Monad, it would take two type parameters, only one
// of which could be inferred, and that' annoying to write Monad.pure[Int,List](1)
class PureOp[A](a: A) {
def pure[M[_]](implicit monad: Monad[M]) = monad(a)
}

/**
* This enrichment allows us to use our Monad instances in for expressions:
* if (import Monad._) has been done
*/
class MonadOperators[A, M[A]](m: M[A])(implicit monad: Monad[M]) {
// This is called fmap in haskell (and in Functor, not Monad)
def map[U](fn: (A) => U): M[U] = monad.map(m)(fn)
class MonadOperators[A, M[_]](m: M[A])(implicit monad: Monad[M]) extends ApplicativeOperators[A, M](m) {
def flatMap[U](fn: (A) => M[U]): M[U] = monad.flatMap(m)(fn)
}

Expand Down
19 changes: 16 additions & 3 deletions algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,26 @@ class ListMonoid[T] extends Monoid[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 Some(items.foldRight(Nil: List[T])(_ ::: _))
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())
}
}

/**
Expand Down Expand Up @@ -134,7 +147,7 @@ class Function1Monoid[T] extends Monoid[Function1[T, T]] {
}

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

object OrVal {
implicit def monoid: Monoid[OrVal] = OrValMonoid
Expand All @@ -150,7 +163,7 @@ object OrValMonoid extends Monoid[OrVal] {
}

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

object AndVal {
implicit def monoid: Monoid[AndVal] = AndValMonoid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.twitter.algebird

import java.nio._

case class MurmurHash128(seed: Long) {
case class MurmurHash128(seed: Long) extends AnyVal {
def apply(buffer: ByteBuffer, offset: Int, length: Int): (Long, Long) = {
val longs = CassandraMurmurHash.hash3_x64_128(buffer, offset, length, seed)
(longs(0), longs(1))
Expand Down
Loading

0 comments on commit e3f3fcb

Please sign in to comment.