Skip to content

Instantly share code, notes, and snippets.

@sageserpent-open
Last active February 20, 2026 08:45
Show Gist options
  • Select an option

  • Save sageserpent-open/c57512d884fdca915e323313abb4a499 to your computer and use it in GitHub Desktop.

Select an option

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.
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
}
}
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])
)
}
}
@sageserpent-open
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment