coulomb-refined

The coulomb-refined package defines algebra typeclasses for integrating the refined typelevel libraries with coulomb.

Quick Start

documentation

You can browse the coulomb-refined api definitions here.

packages

Include coulomb-refined with your Scala project:

libraryDependencies += "com.manyangled" %% "coulomb-core" % "0.9.1"
libraryDependencies += "com.manyangled" %% "coulomb-refined" % "0.9.1"

import

// fundamental coulomb types and methods
import coulomb.*
import coulomb.syntax.*

// common refined definitions
import eu.timepit.refined.*
import eu.timepit.refined.api.*
import eu.timepit.refined.numeric.*

// algebraic definitions
import algebra.instances.all.given

// algebra typeclasses for refined integrations
import coulomb.integrations.refined.all.given

// coulomb syntax for refined integrations
import coulomb.integrations.refined.syntax.*

examples

Examples in this section will use the following workaround as a replacement for refineMV until it is ported forward to Scala 3.

// a workaround for refineMV not being available in scala3
// https://github.com/fthomas/refined/issues/932
object workaround:
    extension [V](v: V)
        def withRP[P](using Validate[V, P]): Refined[V, P] =
            refineV[P].unsafeFrom(v)

import workaround.*

The coulomb-refined package supports refined predicates that are algebraically well-behaved for applicable operations. Primarily this means the predicates Positive and NonNegative. For example, the positive doubles are an additive semigroup and multiplicative group, as the following code demonstrates.

The table below summarizes the full list of supported refined predicates and associated algebras.

import coulomb.units.si.{*, given}
import coulomb.units.us.{*, given}

val pos1 = 1d.withRP[Positive].withUnit[Meter]
// pos1: Quantity[(Double Refined Positive), Meter] = 1.0
val pos2 = 2d.withRP[Positive].withUnit[Meter]
// pos2: Quantity[(Double Refined Positive), Meter] = 2.0
val pos3 = 3d.withRP[Positive].withUnit[Second]
// pos3: Quantity[(Double Refined Positive), Second] = 3.0

// positive doubles are an additive semigroup
pos1 + pos2
// res0: Quantity[(Double Refined Positive), Meter] = 3.0

// also a multiplicative semigroup
pos1 * pos2
// res1: Quantity[(Double Refined Positive), ^[Meter, 2]] = 2.0
pos2.pow[2]
// res2: Quantity[(Double Refined Positive), ^[Meter, 2]] = 4.0

// also a multiplicative group
pos2 / pos3
// res3: Quantity[(Double Refined Positive), /[Meter, Second]] = 0.6666666666666666
pos2.pow[0]
// res4: Quantity[(Double Refined Positive), 1] = 1.0

The standard refined function for refining values with run-time checking is refineV, which returns an Either. The coulomb-refined package supplies a similar variation refinedVU. These objects are also supported by algebras.

// This refinement succeeds, and returns a Right value
val pe1 = refineVU[Positive, Meter](1)
// pe1: Quantity[Either[String, (Int Refined Positive)], Meter] = Right(
//   value = 1
// )

// This refinement fails, and returns a Left value
val pe2 = refineVU[Positive, Meter](0)
// pe2: Quantity[Either[String, (Int Refined Positive)], Meter] = Left(
//   value = "Predicate failed: (0 > 0)."
// )

// positives are an additive semigroup
pe1 + pe1
// res5: Quantity[Either[String, (Int Refined Positive)], Meter] = Right(
//   value = 2
// )

// algebras operating on Left values result in a Left
pe1 + pe2
// res6: Quantity[Either[String, (Int Refined Positive)], Meter] = Left(
//   value = "Predicate failed: (0 > 0)."
// )

algebra support table

The following table summarizes the algebras and operations supported by this package. Examples of Fractional value types include Double, Float, BigDecimal, spire Rational, etc. Integral value types include Int, Long, BigInt, etc.

Value Type Predicate Add Alg Mult Alg + * / pow (exponent)
Fractional Positive semigroup group Y Y Y Y (rational)
Fractional NonNegative semigroup semigroup Y Y N Y (pos int)
Integral Positive semigroup semigroup Y Y N Y (pos int)
Integral NonNegative semigroup semigroup Y Y N Y (pos int)

The table above also applies to Either objects returned by refineVU as discussed in the examples section above.