Last active
February 20, 2026 08:45
-
-
Save sageserpent-open/c57512d884fdca915e323313abb4a499 to your computer and use it in GitHub Desktop.
A lifted ordered type in Scala 2.13 that extends some underlying ordered type `X` with negative and positive infinities.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.sageserpent.gist | |
| import scala.language.implicitConversions | |
| object Unbounded { | |
| implicit def ordering[X: Ordering]: Ordering[Unbounded[X]] = | |
| (first: Unbounded[X], second: Unbounded[X]) => | |
| (first, second) match { | |
| case (Finite(thisUnlifted), Finite(anotherUnlifted)) => | |
| Ordering[X].compare(thisUnlifted, anotherUnlifted) | |
| case (PositiveInfinity, PositiveInfinity) => 0 | |
| case (NegativeInfinity, NegativeInfinity) => 0 | |
| case (_, PositiveInfinity) => -1 | |
| case (NegativeInfinity, _) => -1 | |
| case (PositiveInfinity, _) => 1 | |
| case (_, NegativeInfinity) => 1 | |
| } | |
| implicit def orderedSyntaxFor[X: Ordering]( | |
| unbounded: Unbounded[X] | |
| ): Ordered[Unbounded[X]] = | |
| scala.math.Ordered.orderingToOrdered[Unbounded[X]](unbounded) | |
| } | |
| sealed trait Unbounded[+X] | |
| case class Finite[X: Ordering](unlifted: X) | |
| extends Unbounded[X] | |
| with Ordered[Unbounded[X]] { | |
| override def compare(that: Unbounded[X]): Int = | |
| Unbounded.ordering.compare(this, that) | |
| } | |
| case object NegativeInfinity | |
| extends Unbounded[Nothing] | |
| with Ordered[Unbounded[_ <: Any]] { | |
| override def compare(that: Unbounded[_]): Int = that match { | |
| case NegativeInfinity => 0 | |
| case _ => -1 | |
| } | |
| } | |
| case object PositiveInfinity | |
| extends Unbounded[Nothing] | |
| with Ordered[Unbounded[_ <: Any]] { | |
| override def compare(that: Unbounded[_]): Int = that match { | |
| case PositiveInfinity => 0 | |
| case _ => 1 | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.sageserpent.gist | |
| import com.eed3si9n.expecty.Expecty.assert | |
| import com.sageserpent.americium.Trials.api | |
| import com.sageserpent.americium.junit5.* | |
| import org.junit.jupiter.api.{Test, TestFactory} | |
| class UnboundedSuite { | |
| private val integerTrials = api.integers | |
| private implicit val ordering: Ordering[Unbounded[Int]] = | |
| Unbounded.ordering[Int] | |
| // This test proves that `Unbounded[X]` respects the same order as the | |
| // underlying `X`. So there is no need to prove the axioms about reflexivity, | |
| // anticommutativity and transitivity, as these come for free via | |
| // `Ordering[X]`. The only loophole is how the infinite values fit into the | |
| // ordering; that is covered by the other tests. | |
| @TestFactory | |
| def liftedFiniteValuesShouldBeOrderedToTheUnderlyingFiniteValues() | |
| : DynamicTests = | |
| (integerTrials and integerTrials).withLimit(100).dynamicTests { | |
| (firstUnderlying, secondUnderlying) => | |
| // First, via `Ordering`... | |
| assert( | |
| Ordering[Unbounded[Int]].compare( | |
| Finite(firstUnderlying), | |
| Finite(secondUnderlying) | |
| ) == firstUnderlying.compare(secondUnderlying) | |
| ) | |
| assert( | |
| Ordering[Finite[Int]].compare( | |
| Finite(firstUnderlying), | |
| Finite(secondUnderlying) | |
| ) == firstUnderlying.compare(secondUnderlying) | |
| ) | |
| // Second, via `Ordered` as a syntax enhancement... | |
| assert( | |
| Finite(firstUnderlying).compare( | |
| Finite( | |
| secondUnderlying | |
| ) | |
| ) == firstUnderlying.compare(secondUnderlying) | |
| ) | |
| assert( | |
| Finite(firstUnderlying).compare( | |
| Finite( | |
| secondUnderlying | |
| ): Unbounded[Int] | |
| ) == firstUnderlying.compare(secondUnderlying) | |
| ) | |
| assert( | |
| (Finite(firstUnderlying): Unbounded[Int]).compare( | |
| Finite( | |
| secondUnderlying | |
| ) | |
| ) == firstUnderlying.compare(secondUnderlying) | |
| ) | |
| } | |
| @TestFactory | |
| def negativeInfinityShouldBeLessThanAllFiniteValues(): DynamicTests = | |
| integerTrials | |
| .withLimit(100) | |
| .dynamicTests { underlying => | |
| // First, via `Ordering`... | |
| assert( | |
| Ordering[Unbounded[Int]].lt(NegativeInfinity, Finite(underlying)) | |
| ) | |
| assert( | |
| Ordering[Unbounded[Int]].gt(Finite(underlying), NegativeInfinity) | |
| ) | |
| // Second, via `Ordered` as a syntax enhancement... | |
| assert( | |
| NegativeInfinity < Finite(underlying) | |
| ) | |
| assert( | |
| NegativeInfinity < (Finite(underlying): Unbounded[Int]) | |
| ) | |
| assert( | |
| (NegativeInfinity: Unbounded[Int]) < Finite(underlying) | |
| ) | |
| assert( | |
| (NegativeInfinity: Unbounded[Int]) < (Finite(underlying): Unbounded[ | |
| Int | |
| ]) | |
| ) | |
| assert( | |
| Finite(underlying) > NegativeInfinity | |
| ) | |
| assert( | |
| Finite(underlying) > (NegativeInfinity: Unbounded[Int]) | |
| ) | |
| assert( | |
| (Finite(underlying): Unbounded[Int]) > NegativeInfinity | |
| ) | |
| assert( | |
| (Finite(underlying): Unbounded[Int]) > (NegativeInfinity: Unbounded[ | |
| Int | |
| ]) | |
| ) | |
| } | |
| @TestFactory | |
| def positiveInfinityShouldBeGreaterThanAllFiniteValues(): DynamicTests = | |
| integerTrials | |
| .withLimit(100) | |
| .dynamicTests { underlying => | |
| // First, via `Ordering`... | |
| assert( | |
| Ordering[Unbounded[Int]].gt(PositiveInfinity, Finite(underlying)) | |
| ) | |
| assert( | |
| Ordering[Unbounded[Int]].lt(Finite(underlying), PositiveInfinity) | |
| ) | |
| // Second, via `Ordered` as a syntax enhancement... | |
| assert( | |
| PositiveInfinity > Finite(underlying) | |
| ) | |
| assert( | |
| PositiveInfinity > (Finite(underlying): Unbounded[Int]) | |
| ) | |
| assert( | |
| (PositiveInfinity: Unbounded[Int]) > Finite(underlying) | |
| ) | |
| assert( | |
| (PositiveInfinity: Unbounded[Int]) > (Finite(underlying): Unbounded[ | |
| Int | |
| ]) | |
| ) | |
| assert( | |
| Finite(underlying) < PositiveInfinity | |
| ) | |
| assert( | |
| Finite(underlying) < (PositiveInfinity: Unbounded[Int]) | |
| ) | |
| assert( | |
| (Finite(underlying): Unbounded[Int]) < PositiveInfinity | |
| ) | |
| assert( | |
| (Finite(underlying): Unbounded[Int]) < (PositiveInfinity: Unbounded[ | |
| Int | |
| ]) | |
| ) | |
| } | |
| @Test | |
| def negativeInfinityShouldBeLessThanPositiveInfinity(): Unit = { | |
| // First, via `Ordering`... | |
| assert(Ordering[Unbounded[Int]].lt(NegativeInfinity, PositiveInfinity)) | |
| assert(Ordering[Unbounded[Int]].gt(PositiveInfinity, NegativeInfinity)) | |
| // Second, via `Ordered` as a syntax enhancement... | |
| assert(NegativeInfinity < PositiveInfinity) | |
| assert(NegativeInfinity < (PositiveInfinity: Unbounded[Int])) | |
| assert((NegativeInfinity: Unbounded[Int]) < PositiveInfinity) | |
| assert( | |
| (NegativeInfinity: Unbounded[Int]) < (PositiveInfinity: Unbounded[Int]) | |
| ) | |
| assert(PositiveInfinity > NegativeInfinity) | |
| assert(PositiveInfinity > (NegativeInfinity: Unbounded[Int])) | |
| assert((PositiveInfinity: Unbounded[Int]) > NegativeInfinity) | |
| assert( | |
| (PositiveInfinity: Unbounded[Int]) > (NegativeInfinity: Unbounded[Int]) | |
| ) | |
| } | |
| @Test | |
| def negativeInfinityShouldBeEqualToItself(): Unit = { | |
| // First, via `Ordering`... | |
| assert( | |
| Ordering[NegativeInfinity.type].equiv(NegativeInfinity, NegativeInfinity) | |
| ) | |
| assert( | |
| Ordering[Unbounded[Int]].equiv(NegativeInfinity, NegativeInfinity) | |
| ) | |
| // Second, via `Ordered` as a syntax enhancement... | |
| assert( | |
| NegativeInfinity == NegativeInfinity | |
| ) | |
| assert( | |
| (NegativeInfinity: Unbounded[Int]) == NegativeInfinity | |
| ) | |
| assert( | |
| (NegativeInfinity: Unbounded[Int]) == (NegativeInfinity: Unbounded[Int]) | |
| ) | |
| } | |
| @Test | |
| def positiveInfinityShouldBeEqualToItself(): Unit = { | |
| // First, via `Ordering`... | |
| assert( | |
| Ordering[PositiveInfinity.type].equiv(PositiveInfinity, PositiveInfinity) | |
| ) | |
| assert( | |
| Ordering[Unbounded[Int]].equiv(PositiveInfinity, PositiveInfinity) | |
| ) | |
| // Second, via `Ordered` as a syntax enhancement... | |
| assert( | |
| PositiveInfinity == PositiveInfinity | |
| ) | |
| assert( | |
| (PositiveInfinity: Unbounded[Int]) == PositiveInfinity | |
| ) | |
| assert( | |
| (PositiveInfinity: Unbounded[Int]) == (PositiveInfinity: Unbounded[Int]) | |
| ) | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This used to live in the Americium repository as unused legacy code (https://github.com/sageserpent-open/americium/blob/master/src/main/scala/com/sageserpent/americium/Unbounded.scala).
Its test suite uses Americium and JUnit5.
Leaving it here as it is no longer relevant to Americium's mission statement.
Feel free to copy and adapt it.