Skip to content

Commit

Permalink
more docs
Browse files Browse the repository at this point in the history
  • Loading branch information
erikerlandson committed Jun 19, 2022
1 parent dc32618 commit 1c72568
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 11 deletions.
213 changes: 209 additions & 4 deletions core/src/main/scala/coulomb/deltaquantity.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,55 +21,260 @@ import coulomb.ops.*
import coulomb.conversion.*

package syntax {
// lift using withDeltaUnit method
// this has to be in a separated namespace:
// https://github.com/lampepfl/dotty/issues/15255
extension[V](v: V)
/**
* Lift a raw value into a delta-unit quantity
* @tparam U the desired unit type
* @tparam B base unit to anchor with
* @return a DeltaQuantity with given value and unit type
* {{{
* val date = (1.0).withDeltaUnit[Day, Second]
* }}}
*/
inline def withDeltaUnit[U, B]: DeltaQuantity[V, U, B] = DeltaQuantity[U, B](v)
}

/**
* Represents a value with an associated unit type and "delta" offset,
* for example [[coulomb.units.temperature.Temperature]] or [[coulomb.units.time.EpochTime]]
* @tparam V the raw value type
* @tparam U the unit type
* @tparam B base unit type (the base unit of U)
*/
opaque type DeltaQuantity[V, U, B] = V

/**
* Defines DeltaQuantity constructors and extension methods
*/
object DeltaQuantity:
/**
* Lift a raw value of type V into a unit quantity
* @tparam U the desired unit type
* @tparam B base unit type of U
* @return a DeltaQuantity with given value and unit type
* {{{
* val temp = DeltaQuantity[Celsius, Kelvin](100.0)
* }}}
*/
def apply[U, B](using a: Applier[U, B]) = a

abstract class Applier[U, B]:
def apply[V](v: V): DeltaQuantity[V, U, B]
/**
* A shim class for DeltaQuantity companion object constructors
*/
class Applier[U, B]:
def apply[V](v: V): DeltaQuantity[V, U, B] = v
object Applier:
given [U, B]: Applier[U, B] = new Applier[U, B] { def apply[V](v: V): DeltaQuantity[V, U, B] = v }
given [U, B]: Applier[U, B] = new Applier[U, B]

extension[VL, UL, B](ql: DeltaQuantity[VL, UL, B])
/**
* extract the raw value of a delta-unit quantity
* @return the underlying value, stripped of its unit information
* {{{
* val t = (37.0).withTemperature[Celsius]
* t.value // => 37.0
* val d = (1.0).withEpochTime[Week]
* d.value // => 1.0
* }}}
*/
inline def value: VL = ql

/**
* returns a string representing this DeltaQuantity, using unit abbreviations
* @example
* {{{
* val t = (37.0).withTemperature[Celsius]
* t.show // => "37.0 °C"
* }}}
*/
inline def show: String = s"${ql.value.toString} ${showUnit[UL]}"

/**
* returns a string representing this DeltaQuantity, using full unit names
* @example
* {{{
* val t = (37.0).withTemperature[Celsius]
* t.showFull // => "37.0 celsius"
* }}}
*/
inline def showFull: String = s"${ql.value.toString} ${showUnitFull[UL]}"

/**
* convert a delta-quantity to a new value type
* @tparam V the new value type to use
* @return a new `DeltaQuantity` having value type `V`
* @example
* {{{
* val t = 37.withTemperature[Celsius]
* t.toValue[Float] // => Temperature[Float, Celsius](37.0)
* }}}
*/
inline def toValue[V](using conv: ValueConversion[VL, V]): DeltaQuantity[V, UL, B] =
conv(ql.value).withDeltaUnit[UL, B]

/**
* convert a delta-quantity to a new unit type
* @tparam U the new unit type
* @return a new `DeltaQuantity` having unit type `U`
* @note attempting to convert to an incompatible unit will result in a compile error
* @example
* {{{
* val t = 37d.withTemperature[Celsius]
* t.toUnit[Fahrenheit] // => Temperature[Double, Fahrenheit](98.6)
* }}}
*/
inline def toUnit[U](using conv: DeltaUnitConversion[VL, B, UL, U]): DeltaQuantity[VL, U, B] =
conv(ql.value).withDeltaUnit[U, B]

/**
* convert a delta-quantity from a fractional value type to an integer type
* @tparam V the new value type to use
* @return a new `DeltaQuantity` having value type `V`
* @example
* {{{
* val t = (98.6).withTemperature[Fahrenheit]
* t.tToValue[Int] // => Temperature[Int, Fahrenheit](98)
* }}}
*/
inline def tToValue[V](using conv: TruncatingValueConversion[VL, V]): DeltaQuantity[V, UL, B] =
conv(ql.value).withDeltaUnit[UL, B]

/**
* convert a delta-quantity to a new unit type, using an integer value type
* @tparam U the new unit type
* @return a new `DeltaQuantity` having unit type `U`
* @note attempting to convert to an incompatible unit will result in a compile error
* @example
* {{{
* val t = 37.withTemperature[Celsius]
* t.tToUnit[Fahrenheit] // => Temperature[Int, Fahrenheit](98)
* }}}
*/
inline def tToUnit[U](using conv: TruncatingDeltaUnitConversion[VL, B, UL, U]): DeltaQuantity[VL, U, B] =
conv(ql.value).withDeltaUnit[U, B]

/**
* subtract another delta-quantity from this one
* @tparam VR right hand value type
* @tparam UR right hand unit type
* @param qr right hand delta-quantity
* @return the result of subtracting `qr` from this, as a Quantity value
* @example
* {{{
* val t1 = 14.withEpochTime[Day]
* val t2 = (1.0).withEpochTime[Week]
* t1 - t2 // => Quantity[Double, Day](7.0)
* }}}
* @note unit types `UL` and `UR` must be convertable
* @note result may depend on what algebras, policies, and other typeclasses are in scope
*/
transparent inline def -[VR, UR](qr: DeltaQuantity[VR, UR, B])(using sub: DeltaSub[B, VL, UL, VR, UR]): Quantity[sub.VO, sub.UO] =
sub.eval(ql, qr)

/**
* subtract quantity from this delta-quantity
* @tparam VR right hand value type
* @tparam UR right hand unit type
* @param qr right hand quantity
* @return the result of subtracting `qr` from this, as a DeltaQuantity value
* @example
* {{{
* val t1 = 14.withEpochTime[Day]
* val q = (1.0).withUnit[Week]
* t1 - q // => EpochTime[Double, Day](7.0)
* }}}
* @note unit types `UL` and `UR` must be convertable
* @note result may depend on what algebras, policies, and other typeclasses are in scope
*/
transparent inline def -[VR, UR](qr: Quantity[VR, UR])(using sub: DeltaSubQ[B, VL, UL, VR, UR]): DeltaQuantity[sub.VO, sub.UO, B] =
sub.eval(ql, qr)

/**
* add a quantity to this delta-quantity
* @tparam VR right hand value type
* @tparam UR right hand unit type
* @param qr right hand quantity
* @return the result of adding `qr` to this, as a DeltaQuantity value
* @example
* {{{
* val t1 = 14.withEpochTime[Day]
* val q = (1.0).withUnit[Week]
* t1 + q // => EpochTime[Double, Day](21.0)
* }}}
* @note unit types `UL` and `UR` must be convertable
* @note result may depend on what algebras, policies, and other typeclasses are in scope
*/
transparent inline def +[VR, UR](qr: Quantity[VR, UR])(using add: DeltaAddQ[B, VL, UL, VR, UR]): DeltaQuantity[add.VO, add.UO, B] =
add.eval(ql, qr)

/**
* test this delta-quantity for equality with another
* @tparam VR value type of the right hand quantity
* @tparam UR unit type of the right hand quantity
* @param qr the right hand quantity
* @return true if right hand value equals the left (after any conversions), false otherwise
* @example
* {{{
* val t1 = (14.0).withEpochTime[Day]
* val t2 = 2.withEpochTime[Week]
* t1 === t2 // => true
* }}}
* @note result may depend on what algebras, policies, and other typeclasses are in scope
*/
inline def ===[VR, UR](qr: DeltaQuantity[VR, UR, B])(using ord: DeltaOrd[B, VL, UL, VR, UR]): Boolean =
ord(ql, qr) == 0

/**
* test this delta-quantity for inequality with another
* @tparam VR value type of the right hand quantity
* @tparam UR unit type of the right hand quantity
* @param qr the right hand quantity
* @return true if right hand value does not equal the left (after any conversions), false otherwise
* @example
* {{{
* val t1 = (14.0).withEpochTime[Day]
* val t2 = 2.withEpochTime[Week]
* t1 =!= t2 // => false
* }}}
* @note result may depend on what algebras, policies, and other typeclasses are in scope
*/
inline def =!=[VR, UR](qr: DeltaQuantity[VR, UR, B])(using ord: DeltaOrd[B, VL, UL, VR, UR]): Boolean =
ord(ql, qr) != 0

/**
* test if this delta-quantity is less than another
* @tparam VR value type of the right hand quantity
* @tparam UR unit type of the right hand quantity
* @param qr the right hand quantity
* @return true if left-hand value is less than the right (after any conversions), false otherwise
* @example
* {{{
* val t1 = (14.0).withEpochTime[Day]
* val t2 = 3.withEpochTime[Week]
* t1 < t2 // => true
* }}}
* @note result may depend on what algebras, policies, and other typeclasses are in scope
*/
inline def <[VR, UR](qr: DeltaQuantity[VR, UR, B])(using ord: DeltaOrd[B, VL, UL, VR, UR]): Boolean =
ord(ql, qr) < 0

/**
* test if this delta-quantity is less than or equal to than another
* @tparam VR value type of the right hand quantity
* @tparam UR unit type of the right hand quantity
* @param qr the right hand quantity
* @return true if left-hand value is less than or equal to the right (after any conversions),
* false otherwise
* @example
* {{{
* val t1 = (14.0).withEpochTime[Day]
* val t2 = 3.withEpochTime[Week]
* t1 <= t2 // => true
* }}}
* @note result may depend on what algebras, policies, and other typeclasses are in scope
*/
inline def <=[VR, UR](qr: DeltaQuantity[VR, UR, B])(using ord: DeltaOrd[B, VL, UL, VR, UR]): Boolean =
ord(ql, qr) <= 0

Expand Down
14 changes: 7 additions & 7 deletions core/src/main/scala/coulomb/quantity.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ package syntax {
opaque type Quantity[V, U] = V

/**
* Defines Quantity constructors that lift values into unit Quantities
* Defines Quantity constructors and extension methods
*/
object Quantity:
import syntax.withUnit
Expand Down Expand Up @@ -352,7 +352,7 @@ object Quantity:
* @example
* {{{
* val q1 = 1000.withUnit[Liter]
* val q2 = 1.withUnit[Meter ^ 3]
* val q2 = (1.0).withUnit[Meter ^ 3]
* q1 === q2 // => true
* }}}
* @note result may depend on what algebras, policies, and other typeclasses are in scope
Expand All @@ -369,7 +369,7 @@ object Quantity:
* @example
* {{{
* val q1 = 1000.withUnit[Liter]
* val q2 = 2.withUnit[Meter ^ 3]
* val q2 = (2.0).withUnit[Meter ^ 3]
* q1 =!= q2 // => true
* }}}
* @note result may depend on what algebras, policies, and other typeclasses are in scope
Expand All @@ -386,7 +386,7 @@ object Quantity:
* @example
* {{{
* val q1 = 1000.withUnit[Liter]
* val q2 = 2.withUnit[Meter ^ 3]
* val q2 = (2.0).withUnit[Meter ^ 3]
* q1 < q2 // => true
* }}}
* @note result may depend on what algebras, policies, and other typeclasses are in scope
Expand All @@ -404,7 +404,7 @@ object Quantity:
* @example
* {{{
* val q1 = 1000.withUnit[Liter]
* val q2 = 2.withUnit[Meter ^ 3]
* val q2 = (2.0).withUnit[Meter ^ 3]
* q1 <= q2 // => true
* }}}
* @note result may depend on what algebras, policies, and other typeclasses are in scope
Expand All @@ -422,7 +422,7 @@ object Quantity:
* @example
* {{{
* val q1 = 2000.withUnit[Liter]
* val q2 = 1.withUnit[Meter ^ 3]
* val q2 = (1.0).withUnit[Meter ^ 3]
* q1 > q2 // => true
* }}}
* @note result may depend on what algebras, policies, and other typeclasses are in scope
Expand All @@ -440,7 +440,7 @@ object Quantity:
* @example
* {{{
* val q1 = 2000.withUnit[Liter]
* val q2 = 1.withUnit[Meter ^ 3]
* val q2 = (1.0).withUnit[Meter ^ 3]
* q1 >= q2 // => true
* }}}
* @note result may depend on what algebras, policies, and other typeclasses are in scope
Expand Down
21 changes: 21 additions & 0 deletions spire/src/test/scala/coulomb/quantity.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@

import coulomb.testing.CoulombSuite

object repro {
import coulomb.*
import coulomb.syntax.*

import algebra.instances.all.given
import coulomb.ops.algebra.spire.all.given

import coulomb.policy.spire.standard.given
import coulomb.units.si.*
import coulomb.units.si.given
import scala.math._

object ItcImpl2 {
val expTime: Quantity[BigDecimal, Second] = BigDecimal(1).withUnit[Second]
val nExp = 1
val t = nExp.withUnit[Unitless]
val totalTime: Quantity[BigDecimal, Second] =
expTime * nExp.withUnit[Unitless]
}
}

class SpireQuantitySuite extends CoulombSuite:
import spire.math.*
import coulomb.*
Expand Down

0 comments on commit 1c72568

Please sign in to comment.