Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move typelevel/algebra into cats repo #3877

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ functionality, you can pick-and-choose from amongst these modules
* `cats-kernel`: Small set of basic type classes (*required*).
* `cats-core`: Most core type classes and functionality (*required*).
* `cats-laws`: Laws for testing type class instances.
* `cats-algebra`: Type classes to represent algebraic structures.
* `cats-free`: Free structures such as the free monad, and supporting type classes.
* `cats-testkit`: lib for writing tests for type class instances using laws.
* `alleycats-core`: Cats instances and classes which are not lawful.
Expand All @@ -140,10 +141,11 @@ Links:
2. ScalaDoc: [typelevel.org/cats/api/](https://typelevel.org/cats/api/)
3. Type classes: [typelevel.org/cats/typeclasses](https://typelevel.org/cats/typeclasses.html)
4. Data types: [typelevel.org/cats/datatypes.html](https://typelevel.org/cats/datatypes.html)
5. Glossary: [typelevel.org/cats/nomenclature.html](https://typelevel.org/cats/nomenclature.html)
6. Resources for Learners: [typelevel.org/cats/resources_for_learners.html](https://typelevel.org/cats/resources_for_learners.html)
7. FAQ: [typelevel.org/cats/faq.html](https://typelevel.org/cats/faq.html)
8. The Typelevel Ecosystem: [typelevel.org/cats/typelevelEcosystem.html](https://typelevel.org/cats/typelevelEcosystem.html)
5. Algebra overview: [typelevel.org/cats/algebra.html](https://typelevel.org/cats/algebra.html)
6. Glossary: [typelevel.org/cats/nomenclature.html](https://typelevel.org/cats/nomenclature.html)
7. Resources for Learners: [typelevel.org/cats/resources_for_learners.html](https://typelevel.org/cats/resources_for_learners.html)
8. FAQ: [typelevel.org/cats/faq.html](https://typelevel.org/cats/faq.html)
9. The Typelevel Ecosystem: [typelevel.org/cats/typelevelEcosystem.html](https://typelevel.org/cats/typelevelEcosystem.html)

### Community

Expand Down
66 changes: 66 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/Priority.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package cats
package algebra

/**
* Priority is a type class for prioritized implicit search.
*
* This type class will attempt to provide an implicit instance of `P`
* (the preferred type). If that type is not available it will
* fallback to `F` (the fallback type). If neither type is available
* then a `Priority[P, F]` instance will not be available.
*
* This type can be useful for problems where multiple algorithms can
* be used, depending on the type classes available.
*/
sealed trait Priority[+P, +F] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this trait is never actually used, so I think we should scrap it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funny, I make good use of it in my own project and maybe it even got submitted to Scala? typelevel/algebra#67

Anyway, scrapped.


import Priority.{Fallback, Preferred}

def fold[B](f1: P => B)(f2: F => B): B =
this match {
case Preferred(x) => f1(x)
case Fallback(y) => f2(y)
}

def join[U >: P with F]: U =
fold(_.asInstanceOf[U])(_.asInstanceOf[U])

def bimap[P2, F2](f1: P => P2)(f2: F => F2): Priority[P2, F2] =
this match {
case Preferred(x) => Preferred(f1(x))
case Fallback(y) => Fallback(f2(y))
}

def toEither: Either[P, F] =
fold[Either[P, F]](p => Left(p))(f => Right(f))

def isPreferred: Boolean =
fold(_ => true)(_ => false)

def isFallback: Boolean =
fold(_ => false)(_ => true)

def getPreferred: Option[P] =
fold[Option[P]](p => Some(p))(_ => None)

def getFallback: Option[F] =
fold[Option[F]](_ => None)(f => Some(f))
}

object Priority extends FindPreferred {

case class Preferred[P](get: P) extends Priority[P, Nothing]
case class Fallback[F](get: F) extends Priority[Nothing, F]

def apply[P, F](implicit ev: Priority[P, F]): Priority[P, F] = ev
}

private[algebra] trait FindPreferred extends FindFallback {
implicit def preferred[P](implicit ev: P): Priority[P, Nothing] =
Priority.Preferred(ev)
}

private[algebra] trait FindFallback {
implicit def fallback[F](implicit ev: F): Priority[Nothing, F] =
Priority.Fallback(ev)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cats
package algebra.instances

import scala.annotation.tailrec

object StaticMethods {

/**
* Exponentiation function, e.g. x^y
*
* If base^ex doesn't fit in a Long, the result will overflow (unlike
* Math.pow which will return +/- Infinity).
*/
final def pow(base: Long, exponent: Long): Long = {
@tailrec def loop(t: Long, b: Long, e: Long): Long =
if (e == 0L) t
else if ((e & 1) == 1) loop(t * b, b * b, e >>> 1L)
else loop(t, b * b, e >>> 1L)

if (exponent >= 0L) loop(1L, base, exponent)
else {
if (base == 0L) throw new ArithmeticException("zero can't be raised to negative power")
else if (base == 1L) 1L
else if (base == -1L) if ((exponent & 1L) == 0L) -1L else 1L
else 0L
}
}
}
26 changes: 26 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/all.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cats
package algebra
package instances

package object all extends AllInstances

trait AllInstances
extends ArrayInstances
with BigDecimalInstances
with BigIntInstances
with BitSetInstances
with BooleanInstances
with ByteInstances
with CharInstances
with DoubleInstances
with FloatInstances
with IntInstances
with ListInstances
with LongInstances
with MapInstances
with OptionInstances
with SetInstances
with ShortInstances
with StringInstances
with TupleInstances
with UnitInstances
71 changes: 71 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/array.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cats
package algebra
package instances

import scala.{specialized => sp}

package object array extends ArrayInstances

trait ArrayInstances {
implicit def arrayEq[@sp A: Eq]: Eq[Array[A]] =
new ArrayEq[A]
implicit def arrayOrder[@sp A: Order]: Order[Array[A]] =
new ArrayOrder[A]
implicit def arrayPartialOrder[@sp A: PartialOrder]: PartialOrder[Array[A]] =
new ArrayPartialOrder[A]
}

private object ArraySupport {

private def signum(x: Int): Int =
if (x < 0) -1
else if (x > 0) 1
else 0

def eqv[@sp A](x: Array[A], y: Array[A])(implicit ev: Eq[A]): Boolean = {
var i = 0
if (x.length != y.length) return false
while (i < x.length && i < y.length && ev.eqv(x(i), y(i))) i += 1
i == x.length
}

def compare[@sp A](x: Array[A], y: Array[A])(implicit ev: Order[A]): Int = {
var i = 0
while (i < x.length && i < y.length) {
val cmp = ev.compare(x(i), y(i))
if (cmp != 0) return cmp
i += 1
}
signum(x.length - y.length)
}

def partialCompare[@sp A](x: Array[A], y: Array[A])(implicit ev: PartialOrder[A]): Double = {
var i = 0
while (i < x.length && i < y.length) {
val cmp = ev.partialCompare(x(i), y(i))
// Double.NaN is also != 0.0
if (cmp != 0.0) return cmp
i += 1
}
signum(x.length - y.length).toDouble
}
}

final private class ArrayEq[@sp A: Eq] extends Eq[Array[A]] with Serializable {
def eqv(x: Array[A], y: Array[A]): Boolean =
ArraySupport.eqv(x, y)
}

final private class ArrayOrder[@sp A: Order] extends Order[Array[A]] with Serializable {
override def eqv(x: Array[A], y: Array[A]): Boolean =
ArraySupport.eqv(x, y)
def compare(x: Array[A], y: Array[A]): Int =
ArraySupport.compare(x, y)
}

final private class ArrayPartialOrder[@sp A: PartialOrder] extends PartialOrder[Array[A]] with Serializable {
override def eqv(x: Array[A], y: Array[A]): Boolean =
ArraySupport.eqv(x, y)
override def partialCompare(x: Array[A], y: Array[A]): Double =
ArraySupport.partialCompare(x, y)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cats
package algebra
package instances

import java.math.MathContext

import cats.algebra.ring._

package object bigDecimal extends BigDecimalInstances

trait BigDecimalInstances extends cats.kernel.instances.BigDecimalInstances {
implicit val bigDecimalAlgebra: BigDecimalAlgebra = new BigDecimalAlgebra()
}

class BigDecimalAlgebra(mc: MathContext) extends Field[BigDecimal] with Serializable {
def this() = this(MathContext.UNLIMITED)

val zero: BigDecimal = BigDecimal(0, mc)
val one: BigDecimal = BigDecimal(1, mc)

def plus(a: BigDecimal, b: BigDecimal): BigDecimal = a + b
def negate(a: BigDecimal): BigDecimal = -a
override def minus(a: BigDecimal, b: BigDecimal): BigDecimal = a - b

def times(a: BigDecimal, b: BigDecimal): BigDecimal = a * b
def div(a: BigDecimal, b: BigDecimal): BigDecimal = a / b

override def pow(a: BigDecimal, k: Int): BigDecimal = a.pow(k)

override def fromInt(n: Int): BigDecimal = BigDecimal(n, mc)
override def fromBigInt(n: BigInt): BigDecimal = BigDecimal(n, mc)
}
57 changes: 57 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/bigInt.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package cats
package algebra
package instances

import cats.algebra.ring._

package object bigInt extends BigIntInstances

trait BigIntInstances extends cats.kernel.instances.BigIntInstances {
implicit val bigIntAlgebra: BigIntAlgebra =
new BigIntAlgebra
}

class BigIntAlgebra extends EuclideanRing[BigInt] with Serializable {

val zero: BigInt = BigInt(0)
val one: BigInt = BigInt(1)

def plus(a: BigInt, b: BigInt): BigInt = a + b
def negate(a: BigInt): BigInt = -a
override def minus(a: BigInt, b: BigInt): BigInt = a - b

def times(a: BigInt, b: BigInt): BigInt = a * b

override def pow(a: BigInt, k: Int): BigInt = a.pow(k)

override def fromInt(n: Int): BigInt = BigInt(n)
override def fromBigInt(n: BigInt): BigInt = n

override def lcm(a: BigInt, b: BigInt)(implicit ev: Eq[BigInt]): BigInt =
if (a.signum == 0 || b.signum == 0) zero else (a / a.gcd(b)) * b
override def gcd(a: BigInt, b: BigInt)(implicit ev: Eq[BigInt]): BigInt = a.gcd(b)

def euclideanFunction(a: BigInt): BigInt = a.abs

override def equotmod(a: BigInt, b: BigInt): (BigInt, BigInt) = {
val (qt, rt) = a /% b // truncated quotient and remainder
if (rt.signum >= 0) (qt, rt)
else if (b.signum > 0) (qt - 1, rt + b)
else (qt + 1, rt - b)
}

def equot(a: BigInt, b: BigInt): BigInt = {
val (qt, rt) = a /% b // truncated quotient and remainder
if (rt.signum >= 0) qt
else if (b.signum > 0) qt - 1
else qt + 1
}

def emod(a: BigInt, b: BigInt): BigInt = {
val rt = a % b // truncated remainder
if (rt.signum >= 0) rt
else if (b > 0) rt + b
else rt - b
}

}
22 changes: 22 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/bitSet.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cats
package algebra
package instances

import scala.collection.immutable.BitSet

import cats.algebra.lattice._

package object bitSet extends BitSetInstances

trait BitSetInstances extends cats.kernel.instances.BitSetInstances {
implicit val bitSetAlgebra: BitSetAlgebra =
new BitSetAlgebra
}

class BitSetAlgebra extends GenBool[BitSet] with Serializable {
val zero: BitSet = BitSet.empty
def and(a: BitSet, b: BitSet): BitSet = a & b
def or(a: BitSet, b: BitSet): BitSet = a | b
def without(a: BitSet, b: BitSet): BitSet = a -- b
override def xor(a: BitSet, b: BitSet): BitSet = a ^ b
}
43 changes: 43 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/boolean.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cats
package algebra
package instances

import cats.algebra.lattice.Bool
import cats.algebra.ring.BoolRing
import cats.algebra.ring.CommutativeRig

package object boolean extends BooleanInstances

trait BooleanInstances extends cats.kernel.instances.BooleanInstances {
implicit val booleanAlgebra: BooleanAlgebra =
new BooleanAlgebra

val booleanRing = new BoolRing[Boolean] {
def zero: Boolean = false
def one: Boolean = true
def plus(x: Boolean, y: Boolean): Boolean = x ^ y
def times(x: Boolean, y: Boolean): Boolean = x && y
}
}

/**
* This commutative rig is different than the one obtained from GF(2).
*
* It uses || for plus, and && for times.
*/
class BooleanAlgebra extends Bool[Boolean] with CommutativeRig[Boolean] {

def zero: Boolean = false
def one: Boolean = true

override def isZero(x: Boolean)(implicit ev: Eq[Boolean]): Boolean = !x
override def isOne(x: Boolean)(implicit ev: Eq[Boolean]): Boolean = x

def and(x: Boolean, y: Boolean): Boolean = x && y
def or(x: Boolean, y: Boolean): Boolean = x || y
def complement(x: Boolean): Boolean = !x

def plus(a: Boolean, b: Boolean): Boolean = a || b
override def pow(a: Boolean, b: Int): Boolean = a
override def times(a: Boolean, b: Boolean): Boolean = a && b
}
Loading