Type classes for algebraic structures including basic algebraic structures, ring-like structures, and lattice-like structures in functional programming
The algebra library includes an advanced implicit resolution system for composable type class derivation. The Priority system allows fine-grained control over instance selection and provides fallback behavior for complex derivation scenarios.
The core Priority type represents a prioritized choice between a preferred and fallback value.
/**
* Prioritized implicit search mechanism
* Represents either a preferred value (P) or fallback value (F)
*/
sealed trait Priority[+P, +F] {
/** Fold over the priority value */
def fold[B](f1: P => B)(f2: F => B): B
/** Join operation combining both types */
def join[U >: P with F]: U
/** Transform both preferred and fallback types */
def bimap[P2, F2](f1: P => P2)(f2: F => F2): Priority[P2, F2]
/** Convert to Either (preferred on left, fallback on right) */
def toEither: Either[P, F]
/** Test if this is a preferred value */
def isPreferred: Boolean
/** Test if this is a fallback value */
def isFallback: Boolean
/** Extract preferred value if present */
def getPreferred: Option[P]
/** Extract fallback value if present */
def getFallback: Option[F]
}Represents a preferred implicit resolution.
/**
* Preferred implicit resolution with highest priority
*/
case class Preferred[P](get: P) extends Priority[P, Nothing]Represents a fallback implicit resolution.
/**
* Fallback implicit resolution with lower priority
*/
case class Fallback[F](get: F) extends Priority[Nothing, F]Companion object providing construction and utility methods.
/**
* Priority companion object
*/
object Priority {
/** Get a Priority instance from implicit scope */
def apply[P, F](implicit ev: Priority[P, F]): Priority[P, F]
}Usage Example:
import algebra.Priority
import algebra.Priority._
// Create preferred priority
val preferred: Priority[String, Int] = Preferred("high priority")
val fallback: Priority[String, Int] = Fallback(42)
// Pattern matching
preferred match {
case Preferred(value) => println(s"Got preferred: $value")
case Fallback(value) => println(s"Got fallback: $value")
}
// Functional operations
val result = preferred.fold(
pref => s"Preferred: $pref",
fall => s"Fallback: $fall"
) // "Preferred: high priority"
// Convert to Either
val either = preferred.toEither // Right("high priority")
// Test priority level
val isPreferred = preferred.isPreferred // true
val isFallback = preferred.isFallback // falseInternal trait for finding preferred instances.
/**
* Private trait extending FindFallback for preferred instance resolution
* Used internally by the Priority system for implicit resolution
*/
private trait FindPreferred extends FindFallbackInternal trait for finding fallback instances.
/**
* Private trait for fallback instance resolution
* Used internally by the Priority system for implicit resolution
*/
private trait FindFallbackThe Priority system is commonly used for automatic type class derivation where multiple valid instances might exist:
import algebra._
import algebra.Priority._
// Example: Deriving Monoid instances with priority
trait MonoidDerivation {
// Preferred: Direct instance
implicit def directMonoid[A](implicit ev: Monoid[A]): Preferred[Monoid[A]] =
Preferred(ev)
// Fallback: Derived from Semigroup + identity
implicit def derivedMonoid[A](implicit
semi: Semigroup[A],
zero: Zero[A]
): Fallback[Monoid[A]] =
Fallback(new Monoid[A] {
def empty: A = zero.zero
def combine(x: A, y: A): A = semi.combine(x, y)
})
}
// Usage prioritizes direct instances over derived ones
def useMonoid[A](implicit priority: Priority[Monoid[A], Monoid[A]]): Monoid[A] =
priority.fold(identity, identity)Priority system helps resolve ambiguous implicits:
import algebra.Priority._
trait MultipleInstancesExample {
// High priority instance
implicit def highPriorityString: Preferred[String] = Preferred("high")
// Low priority instance
implicit def lowPriorityString: Fallback[String] = Fallback("low")
// Resolution will pick preferred over fallback
def resolve(implicit priority: Priority[String, String]): String =
priority.fold(preferred = identity, fallback = identity)
}
val example = new MultipleInstancesExample {}
val result = example.resolve // "high" (preferred wins)Priority enables composable derivation strategies:
import algebra._
import algebra.ring._
import algebra.Priority._
trait ComposableDerivation {
// Strategy 1: Direct Ring instance (highest priority)
implicit def directRing[A](implicit ev: Ring[A]): Preferred[Ring[A]] =
Preferred(ev)
// Strategy 2: Construct from components (fallback)
implicit def constructedRing[A](implicit
add: AdditiveCommutativeGroup[A],
mult: MultiplicativeMonoid[A],
// Additional constraints...
): Fallback[Ring[A]] =
Fallback(new Ring[A] {
def zero: A = add.zero
def one: A = mult.one
def plus(x: A, y: A): A = add.plus(x, y)
def times(x: A, y: A): A = mult.times(x, y)
def negate(x: A): A = add.negate(x)
def fromInt(n: Int): A = ??? // Implementation
def fromBigInt(n: BigInt): A = ??? // Implementation
})
}Priority system aids in debugging implicit resolution:
import algebra.Priority._
trait DebuggingExample {
def debugPriority[P, F](implicit priority: Priority[P, F]): Unit = {
priority match {
case Preferred(value) =>
println(s"Using preferred instance: ${value.getClass.getSimpleName}")
case Fallback(value) =>
println(s"Using fallback instance: ${value.getClass.getSimpleName}")
}
}
// Usage
def example[A](implicit priority: Priority[Ring[A], Ring[A]]): Ring[A] = {
debugPriority(priority) // Shows which instance was selected
priority.fold(identity, identity)
}
}Priorities can be nested for complex resolution strategies:
import algebra.Priority._
trait NestedPriorities {
type HighPriority[A] = Preferred[A]
type MediumPriority[A] = Fallback[Preferred[A]]
type LowPriority[A] = Fallback[Fallback[A]]
def resolveTiered[A](implicit
priority: Priority[HighPriority[A], Priority[MediumPriority[A], LowPriority[A]]]
): A = {
priority.fold(
high = _.get,
fallback = _.fold(
medium = _.get.get,
low = _.get.get.get
)
)
}
}Priority enables context-sensitive instance derivation:
import algebra.Priority._
trait ContextSensitive {
// Context marker traits
sealed trait FastContext
sealed trait PreciseContext
// Different strategies for different contexts
implicit def fastRing[A](implicit
ctx: FastContext,
base: Ring[A]
): Preferred[Ring[A]] = Preferred(base) // Use base implementation
implicit def preciseRing[A](implicit
ctx: PreciseContext,
enhanced: EnhancedRing[A]
): Preferred[Ring[A]] = Preferred(enhanced.asRing) // Use enhanced version
// Fallback when no context
implicit def defaultRing[A](implicit base: Ring[A]): Fallback[Ring[A]] =
Fallback(base)
}The Priority system integrates seamlessly with algebra's type class hierarchy:
import algebra._
import algebra.instances.all._
import algebra.Priority._
// The system automatically selects the best available instance
def useAlgebraicStructure[A](implicit
priority: Priority[Ring[A], Priority[Monoid[A], Semigroup[A]]]
): String = {
priority.fold(
ring => "Using Ring operations",
fallback => fallback.fold(
monoid => "Using Monoid operations",
semigroup => "Using Semigroup operations"
)
)
}
// Usage examples
useAlgebraicStructure[Int] // "Using Ring operations"
useAlgebraicStructure[String] // "Using Monoid operations"
useAlgebraicStructure[List[Int]] // "Using Monoid operations"import algebra.Priority._
trait CustomDerivation {
// Rule: Prefer CommutativeRing over Ring when available
implicit def commutativeRingPriority[A](implicit
cr: CommutativeRing[A]
): Preferred[Ring[A]] = Preferred(cr)
// Rule: Use regular Ring as fallback
implicit def regularRingFallback[A](implicit
r: Ring[A]
): Fallback[Ring[A]] = Fallback(r)
// Consumer gets the best available Ring
def optimizedRingOp[A](implicit
priority: Priority[Ring[A], Ring[A]]
): Ring[A] = priority.fold(identity, identity)
}This Priority system enables sophisticated implicit resolution strategies while maintaining type safety and predictable behavior in complex algebraic derivation scenarios.
Install with Tessl CLI
npx tessl i tessl/maven-org-typelevel--algebra-2-13