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

Bring Ring-like structures to ZIO Prelude #351

Merged
merged 60 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
37847e1
Ring
sideeffffect Oct 20, 2020
438eb27
renaming
sideeffffect Oct 20, 2020
8b7cbd9
Split into multiple traits
sideeffffect Oct 22, 2020
a0a0fb9
cleanup
sideeffffect Oct 22, 2020
0cbcbcb
improve image
sideeffffect Oct 22, 2020
2c56266
from* methods and private Rind and Field implementation classes
sideeffffect Oct 23, 2020
4a4bcc5
Adjust diagram
sideeffffect Oct 23, 2020
b2dc634
fix
sideeffffect Oct 23, 2020
08abefe
Improve naming and fix Dotty build
sideeffffect Oct 23, 2020
9119db6
Shape
sideeffffect Oct 28, 2020
37eb082
Merge branch 'master' into ring
sideeffffect Nov 8, 2020
be988b9
Rename AnnihilatingZero to Annihilation
sideeffffect Nov 8, 2020
4f4739c
Move type parameters to type members, use the Aux pattern
sideeffffect Nov 8, 2020
6c720bb
Merge branch 'master' into ring
sideeffffect Dec 6, 2020
1483393
fix merge
sideeffffect Dec 6, 2020
ec7aa77
fix diagram
sideeffffect Dec 7, 2020
f0d9ff9
multiplyBy
sideeffffect Dec 7, 2020
1b2abf9
Move to experimental
sideeffffect Dec 7, 2020
da7e552
Merge remote-tracking branch 'upstream/master' into ring
sideeffffect Dec 9, 2020
1b71f20
Remove unnecessary `unwrap`s
sideeffffect Dec 10, 2020
d91a8b1
Merge remote-tracking branch 'upstream/master' into ring
sideeffffect Dec 15, 2020
c163ac4
PartialInverse
sideeffffect Dec 16, 2020
59039e4
Merge remote-tracking branch 'upstream/master' into ring
sideeffffect Dec 17, 2020
dc59476
fix
sideeffffect Dec 17, 2020
b6ac2e5
Merge branch 'master' into ring
sideeffffect Jan 17, 2021
686dbe3
Merge branch 'master' into ring
sideeffffect Jan 21, 2021
c2e3729
test double
sideeffffect Jan 21, 2021
259b73d
Merge branch 'master' into ring
sideeffffect Jan 28, 2021
1e0d8f9
Merge branch 'master' into ring
sideeffffect Jan 30, 2021
aab2a99
Merge branch 'master' into ring
sideeffffect Feb 4, 2021
f521178
fix diagrams
sideeffffect Feb 4, 2021
cf03a10
Merge branch 'master' into ring
sideeffffect Mar 19, 2021
ccf9258
merge fix
sideeffffect Mar 19, 2021
b2eef64
Lazy laws
sideeffffect Mar 19, 2021
789a743
Merge remote-tracking branch 'upstream/master' into ring
sideeffffect Jun 16, 2021
5a9bfee
Add DistributiveMultiply instances for Cause and ParSeq
sideeffffect Jun 16, 2021
74b6fc1
Add DistributiveMultiply Cause and ParSeq tests
sideeffffect Jun 16, 2021
c3a79c5
Cause distributive multiply temporarily disabled
sideeffffect Jun 24, 2021
ad85003
AddMultiplyShape: explicit type annotations in Cause and ParSeq
sideeffffect Jun 25, 2021
6346780
AddMultiplyShape: explicit type annotations in Cause and ParSeq, l.&&…
sideeffffect Jun 25, 2021
feeba2a
AddMultiplyShape: explicit type annotations in Cause and ParSeq, expl…
sideeffffect Jun 25, 2021
8afbb75
Merge branch 'master' into ring
sideeffffect Oct 24, 2021
c420a2f
Merge fix
sideeffffect Oct 25, 2021
93a6cd6
Simplify coherent.scala
sideeffffect Oct 25, 2021
5bbb234
DistributiveMultiply as top
sideeffffect Oct 25, 2021
e53211d
Fix tests
sideeffffect Oct 25, 2021
1dfb4b2
Ring without the reification, i.e. abstract (#2)
sideeffffect Oct 28, 2021
147fc2a
Merge branch 'master' into ring
sideeffffect Oct 29, 2021
0969bd7
Merge branch 'master' into ring
sideeffffect Oct 30, 2021
6818732
Merge fix
sideeffffect Oct 30, 2021
eb54399
Merge branch 'master' into ring
sideeffffect May 27, 2022
1c247c2
Merge branch 'refs/heads/series/2.x' into ring
sideeffffect May 7, 2024
87d216c
fix
sideeffffect May 7, 2024
104e3e4
fix
sideeffffect May 7, 2024
d60de30
Gen.const(new Throwable with NoStackTrace {})
sideeffffect May 7, 2024
3f1f319
More PartialInverseSpec tests
sideeffffect May 7, 2024
5e1e141
More DistributiveProd instances
sideeffffect May 7, 2024
f7fcc79
fix
sideeffffect May 7, 2024
d09e901
Add instances to abstraction-diagrams.md
sideeffffect May 8, 2024
48e588a
implicit lazy val
sideeffffect May 12, 2024
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
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import BuildHelper._

Global / onChangedBuildSource := ReloadOnSourceChanges
Global / concurrentRestrictions += Tags.limit(NativeTags.Link, 1)

inThisBuild(
List(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ object CoherentSpec extends ZIOBaseSpec {
test("EqualInverse") {
val instance = implicitly[EqualInverse[Sum[Int]]]
assert(Sum(42).inverse(Sum(20)))(equalTo(Sum(22))) &&
assert(Sum(2).multiply(5))(equalTo(Sum(10))) &&
assert(Sum(2).multiply(0))(equalTo(Sum(0))) &&
assert(Sum(2).multiply(-5))(equalTo(Sum(-10))) &&
assert(Sum(2).multiplyBy(5))(equalTo(Sum(10))) &&
assert(Sum(2).multiplyBy(0))(equalTo(Sum(0))) &&
assert(Sum(2).multiplyBy(-5))(equalTo(Sum(-10))) &&
assert(instance.multiplyOption(5)(Sum(2)))(equalTo[Option[Sum[Int]]](Some(Sum(10)))) &&
assert(instance.multiplyOption(0)(Sum(2)))(equalTo[Option[Sum[Int]]](Some(Sum(0)))) &&
assert(instance.multiplyOption(-5)(Sum(2)))(equalTo[Option[Sum[Int]]](Some(Sum(-10))))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package zio.prelude

import zio.prelude.laws._
import zio.prelude.newtypes.Prod
import zio.test._
import zio.test.laws._

object PartialInverseSpec extends ZIOBaseSpec {

private implicit val DoubleEqual: Equal[Double] = Equal.DoubleEqualWithEpsilon()
private implicit val FloatEqual: Equal[Float] = Equal.FloatEqualWithEpsilon()

def spec: Spec[Environment, Any] =
suite("PartialInverseSpec")(
suite("laws")(
test("BigDecimal prod")(
checkAllLaws(PartialInverseLaws)(Gen.bigDecimal(BigDecimal(-10), BigDecimal(10)).map(Prod(_)))
),
test("byte prod")(checkAllLaws(PartialInverseLaws)(Gen.byte.map(Prod(_)))),
test("char prod")(checkAllLaws(PartialInverseLaws)(Gen.char.map(Prod(_)))),
test("double prod")(checkAllLaws(PartialInverseLaws)(Gen.double.map(Prod(_)))),
test("float prod")(checkAllLaws(PartialInverseLaws)(Gen.float.map(Prod(_)))),
test("int prod")(checkAllLaws(PartialInverseLaws)(Gen.int.map(Prod(_)))),
test("long prod")(checkAllLaws(PartialInverseLaws)(Gen.long.map(Prod(_)))),
test("short prod")(checkAllLaws(PartialInverseLaws)(Gen.short.map(Prod(_))))
)
)
}
148 changes: 104 additions & 44 deletions core/shared/src/main/scala/zio/prelude/Associative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -183,19 +183,25 @@ object Associative extends AssociativeLowPriority {
}

/**
* The `Commutative`, `Idempotent` instance for the product of `BigDecimal` values
* The `Commutative`, `PartialInverse` instance for the product of `BigDecimal` values
*/
implicit val BigDecimalProdCommutativeIdempotent: Commutative[Prod[BigDecimal]] with Idempotent[Prod[BigDecimal]] =
new Commutative[Prod[BigDecimal]] with Idempotent[Prod[BigDecimal]] {
override def combine(l: => Prod[BigDecimal], r: => Prod[BigDecimal]): Prod[BigDecimal] = Prod(l * r)
implicit val BigDecimalProdCommutativePartialInverse
: Commutative[Prod[BigDecimal]] with PartialInverse[Prod[BigDecimal]] =
new Commutative[Prod[BigDecimal]] with PartialInverse[Prod[BigDecimal]] {
def combine(l: => Prod[BigDecimal], r: => Prod[BigDecimal]): Prod[BigDecimal] = Prod(l * r)
val identity: Prod[BigDecimal] = Prod(BigDecimal(1))
def inverseOption(l: => Prod[BigDecimal], r: => Prod[BigDecimal]): Option[Prod[BigDecimal]] =
if (r != BigDecimal(0)) Some(Prod(l / r)) else None
}

/**
* The `Commutative`, `Idempotent` instance for the sum of `BigDecimal` values
*/
implicit val BigDecimalSumCommutativeIdempotent: Commutative[Sum[BigDecimal]] with Idempotent[Sum[BigDecimal]] =
new Commutative[Sum[BigDecimal]] with Idempotent[Sum[BigDecimal]] {
override def combine(l: => Sum[BigDecimal], r: => Sum[BigDecimal]): Sum[BigDecimal] = Sum(l + r)
implicit val BigDecimalSumCommutativeInverse: Commutative[Sum[BigDecimal]] with Inverse[Sum[BigDecimal]] =
new Commutative[Sum[BigDecimal]] with Inverse[Sum[BigDecimal]] {
def combine(l: => Sum[BigDecimal], r: => Sum[BigDecimal]): Sum[BigDecimal] = Sum(l + r)
val identity: Sum[BigDecimal] = Sum(BigDecimal(0))
def inverse(l: => Sum[BigDecimal], r: => Sum[BigDecimal]): Sum[BigDecimal] = Sum(l - r)
}

/**
Expand Down Expand Up @@ -255,13 +261,15 @@ object Associative extends AssociativeLowPriority {
}

/**
* The `Commutative` and `Identity` instance for the product of `Byte`
* The `Commutative` and `PartialInverse` instance for the product of `Byte`
* values.
*/
implicit val ByteProdCommutativeIdentity: Commutative[Prod[Byte]] with Identity[Prod[Byte]] =
new Commutative[Prod[Byte]] with Identity[Prod[Byte]] {
def combine(l: => Prod[Byte], r: => Prod[Byte]): Prod[Byte] = Prod((l * r).toByte)
val identity: Prod[Byte] = Prod(1)
implicit val ByteProdCommutativePartialInverse: Commutative[Prod[Byte]] with PartialInverse[Prod[Byte]] =
new Commutative[Prod[Byte]] with PartialInverse[Prod[Byte]] {
def combine(l: => Prod[Byte], r: => Prod[Byte]): Prod[Byte] = Prod((l * r).toByte)
val identity: Prod[Byte] = Prod(1)
def inverseOption(l: => Prod[Byte], r: => Prod[Byte]): Option[Prod[Byte]] =
if (r != 0) Some(Prod((l / r).toByte)) else None
}

/**
Expand Down Expand Up @@ -291,13 +299,15 @@ object Associative extends AssociativeLowPriority {
}

/**
* The `Commutative` and `Identity` instance for the product of `Char`
* The `Commutative` and `PartialInverse` instance for the product of `Char`
* values.
*/
implicit val CharProdCommutativeIdentity: Commutative[Prod[Char]] with Identity[Prod[Char]] =
new Commutative[Prod[Char]] with Identity[Prod[Char]] {
def combine(l: => Prod[Char], r: => Prod[Char]): Prod[Char] = Prod((l * r).toChar)
val identity: Prod[Char] = Prod(1)
implicit val CharProdCommutativePartialInverse: Commutative[Prod[Char]] with PartialInverse[Prod[Char]] =
new Commutative[Prod[Char]] with PartialInverse[Prod[Char]] {
def combine(l: => Prod[Char], r: => Prod[Char]): Prod[Char] = Prod((l * r).toChar)
val identity: Prod[Char] = Prod(1)
override def inverseOption(l: => Prod[Char], r: => Prod[Char]): Option[Prod[Char]] =
if (r != 0) Some(Prod((l / r).toChar)) else None
}

/**
Expand Down Expand Up @@ -369,13 +379,15 @@ object Associative extends AssociativeLowPriority {
}

/**
* The `Commutative` and `Identity` instance for the product of `Double`
* The `Commutative` and `PartialInverse` instance for the product of `Double`
* values.
*/
implicit val DoubleProdCommutativeIdentity: Commutative[Prod[Double]] with Identity[Prod[Double]] =
new Commutative[Prod[Double]] with Identity[Prod[Double]] {
def combine(l: => Prod[Double], r: => Prod[Double]): Prod[Double] = Prod(l * r)
val identity: Prod[Double] = Prod(1)
implicit val DoubleProdCommutativePartialInverse: Commutative[Prod[Double]] with PartialInverse[Prod[Double]] =
new Commutative[Prod[Double]] with PartialInverse[Prod[Double]] {
def combine(l: => Prod[Double], r: => Prod[Double]): Prod[Double] = Prod(l * r)
val identity: Prod[Double] = Prod(1)
def inverseOption(l: => Prod[Double], r: => Prod[Double]): Option[Prod[Double]] =
if (r != 0) Some(Prod(l / r)) else None
}

/**
Expand Down Expand Up @@ -431,13 +443,15 @@ object Associative extends AssociativeLowPriority {
}

/**
* The `Commutative` and `Identity` instance for the product of `Float`
* The `Commutative` and `PartialInverse` instance for the product of `Float`
* values.
*/
implicit val FloatProdCommutativeIdentity: Commutative[Prod[Float]] with Identity[Prod[Float]] =
new Commutative[Prod[Float]] with Identity[Prod[Float]] {
def combine(l: => Prod[Float], r: => Prod[Float]): Prod[Float] = Prod(l * r)
val identity: Prod[Float] = Prod(1)
implicit val FloatProdCommutativePartialInverse: Commutative[Prod[Float]] with PartialInverse[Prod[Float]] =
new Commutative[Prod[Float]] with PartialInverse[Prod[Float]] {
def combine(l: => Prod[Float], r: => Prod[Float]): Prod[Float] = Prod(l * r)
val identity: Prod[Float] = Prod(1)
def inverseOption(l: => Prod[Float], r: => Prod[Float]): Option[Prod[Float]] =
if (r != 0) Some(Prod(l / r)) else None
}

/**
Expand Down Expand Up @@ -501,13 +515,15 @@ object Associative extends AssociativeLowPriority {
}

/**
* The `Commutative` and `Identity` instance for the product of `Long`
* The `Commutative` and `PartialInverse` instance for the product of `Long`
* values.
*/
implicit val LongProdCommutativeIdentity: Commutative[Prod[Long]] with Identity[Prod[Long]] =
new Commutative[Prod[Long]] with Identity[Prod[Long]] {
def combine(l: => Prod[Long], r: => Prod[Long]): Prod[Long] = Prod(l * r)
val identity: Prod[Long] = Prod(1)
implicit val LongProdCommutativePartialInverse: Commutative[Prod[Long]] with PartialInverse[Prod[Long]] =
new Commutative[Prod[Long]] with PartialInverse[Prod[Long]] {
def combine(l: => Prod[Long], r: => Prod[Long]): Prod[Long] = Prod(l * r)
val identity: Prod[Long] = Prod(1)
def inverseOption(l: => Prod[Long], r: => Prod[Long]): Option[Prod[Long]] =
if (r != 0) Some(Prod(l / r)) else None
}

/**
Expand Down Expand Up @@ -587,6 +603,21 @@ object Associative extends AssociativeLowPriority {
}
)

implicit def ParSeqProdIdentity[A]: Identity[Prod[ParSeq[Unit, A]]] = new Identity[Prod[ParSeq[Unit, A]]] {
def identity: Prod[ParSeq[Unit, A]] =
Prod(ParSeq.empty)
def combine(l: => Prod[ParSeq[Unit, A]], r: => Prod[ParSeq[Unit, A]]): Prod[ParSeq[Unit, A]] =
Prod(Prod.unwrap(l) ++ Prod.unwrap(r))
}

implicit def ParSeqSumCommutativeIdentity[A]: Commutative[Sum[ParSeq[Unit, A]]] with Identity[Sum[ParSeq[Unit, A]]] =
new Commutative[Sum[ParSeq[Unit, A]]] with Identity[Sum[ParSeq[Unit, A]]] {
def identity: Sum[ParSeq[Unit, A]] =
Sum(ParSeq.empty)
def combine(l: => Sum[ParSeq[Unit, A]], r: => Sum[ParSeq[Unit, A]]): Sum[ParSeq[Unit, A]] =
Sum(Sum.unwrap(l) && Sum.unwrap(r))
}

/**
* The `Commutative` and `Idempotent` instance for the intersection of `Set[A]` values.
*/
Expand Down Expand Up @@ -623,19 +654,21 @@ object Associative extends AssociativeLowPriority {
}

/**
* The `Commutative` and `Identity` instance for the product of `Short`
* The `Commutative` and `PartialInverse` instance for the product of `Short`
* values.
*/
implicit val ShortProdCommutativeIdentity: Commutative[Prod[Short]] with Identity[Prod[Short]] =
new Commutative[Prod[Short]] with Identity[Prod[Short]] {
def combine(l: => Prod[Short], r: => Prod[Short]): Prod[Short] = Prod((l * r).toShort)
val identity: Prod[Short] = Prod(1)
implicit val ShortProdCommutativePartialInverse: Commutative[Prod[Short]] with PartialInverse[Prod[Short]] =
new Commutative[Prod[Short]] with PartialInverse[Prod[Short]] {
def combine(l: => Prod[Short], r: => Prod[Short]): Prod[Short] = Prod((l * r).toShort)
val identity: Prod[Short] = Prod(1)
def inverseOption(l: => Prod[Short], r: => Prod[Short]): Option[Prod[Short]] =
if (r != 0) Some(Prod((l / r).toShort)) else None
}

/**
* The `Commutative` and `Identity` instance for the sum of `Short` values.
* The `Commutative` and `Inverse` instance for the sum of `Short` values.
*/
implicit val ShortSumCommutativeIdentity: Commutative[Sum[Short]] with Inverse[Sum[Short]] =
implicit val ShortSumCommutativeInverse: Commutative[Sum[Short]] with Inverse[Sum[Short]] =
new Commutative[Sum[Short]] with Inverse[Sum[Short]] {
def combine(l: => Sum[Short], r: => Sum[Short]): Sum[Short] = Sum((l + r).toShort)
val identity: Sum[Short] = Sum(0)
Expand Down Expand Up @@ -1388,17 +1421,44 @@ object Associative extends AssociativeLowPriority {
*/
implicit def VectorIdentity[A]: Identity[Vector[A]] =
Identity.make(Vector.empty, _ ++ _)

implicit def zioCauseProdIdentity[A]: Identity[Prod[zio.Cause[A]]] = new Identity[Prod[zio.Cause[A]]] {
def identity: Prod[zio.Cause[A]] =
Prod(zio.Cause.empty)
def combine(l: => Prod[zio.Cause[A]], r: => Prod[zio.Cause[A]]): Prod[zio.Cause[A]] =
Prod(Prod.unwrap(l) ++ Prod.unwrap(r))
}

implicit def zioCauseSumCommutativeIdentity[A]: Commutative[Sum[zio.Cause[A]]] with Identity[Sum[zio.Cause[A]]] =
new Commutative[Sum[zio.Cause[A]]] with Identity[Sum[zio.Cause[A]]] {
def identity: Sum[zio.Cause[A]] =
Sum(zio.Cause.empty)
def combine(l: => Sum[zio.Cause[A]], r: => Sum[zio.Cause[A]]): Sum[zio.Cause[A]] =
Sum(Sum.unwrap(l) && Sum.unwrap(r))
}
}

trait AssociativeLowPriority {

implicit def FxCauseProdAssociative[A]: Associative[Prod[fx.Cause[A]]] = new Associative[Prod[fx.Cause[A]]] {
def combine(l: => Prod[fx.Cause[A]], r: => Prod[fx.Cause[A]]): Prod[fx.Cause[A]] =
Prod(Prod.unwrap(l) ++ Prod.unwrap(r))
}

implicit def FxCauseSumCommutative[A]: Commutative[Sum[fx.Cause[A]]] = new Commutative[Sum[fx.Cause[A]]] {
def combine(l: => Sum[fx.Cause[A]], r: => Sum[fx.Cause[A]]): Sum[fx.Cause[A]] =
Sum(Sum.unwrap(l) && Sum.unwrap(r))
}

/**
* The `Commutative` and `Identity` instance for the product of `Int` values.
* The `Commutative` and `PartialInverse` instance for the product of `Int` values.
*/
implicit val IntProdCommutativeIdentity: Commutative[Prod[Int]] with Identity[Prod[Int]] =
new Commutative[Prod[Int]] with Identity[Prod[Int]] {
def combine(l: => Prod[Int], r: => Prod[Int]): Prod[Int] = Prod(l * r)
val identity: Prod[Int] = Prod(1)
implicit val IntProdCommutativePartialInverse: Commutative[Prod[Int]] with PartialInverse[Prod[Int]] =
new Commutative[Prod[Int]] with PartialInverse[Prod[Int]] {
def combine(l: => Prod[Int], r: => Prod[Int]): Prod[Int] = Prod(l * r)
val identity: Prod[Int] = Prod(1)
def inverseOption(l: => Prod[Int], r: => Prod[Int]): Option[Prod[Int]] =
if (r != 0) Some(Prod(l / r)) else None
}

/**
Expand Down
15 changes: 9 additions & 6 deletions core/shared/src/main/scala/zio/prelude/Inverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ import scala.annotation.tailrec
* natural numbers, since subtracting a number from itself always returns
* zero.
*/
trait Inverse[A] extends Identity[A] {
trait Inverse[A] extends PartialInverse[A] {

def inverse(l: => A, r: => A): A

def multiply(n: Int)(a: A): A = {
final override def inverseOption(l: => A, r: => A): Some[A] = Some(inverse(l, r))

def multiplyBy(n: Int)(a: A): A = {
@tailrec
def multiplyHelper(res: A, n: Int): A =
if (n == 0) res
Expand All @@ -46,8 +49,8 @@ trait Inverse[A] extends Identity[A] {
multiplyHelper(identity, n)
}

override def multiplyOption(n: Int)(a: A): Some[A] =
Some(multiply(n)(a))
final override def multiplyOption(n: Int)(a: A): Some[A] =
Some(multiplyBy(n)(a))
}

object Inverse {
Expand Down Expand Up @@ -903,7 +906,7 @@ trait InverseSyntax {
/**
* Multiplies value 'n' times
*/
def multiply(n: Int)(implicit inverse: Inverse[A]): A =
inverse.multiply(n)(l)
def multiplyBy(n: Int)(implicit inverse: Inverse[A]): A =
inverse.multiplyBy(n)(l)
}
}
Loading