Libra is a dimensional analysis library based on shapeless, spire and singleton-ops. It contains out of the box support for SI units for all numeric types.

To Use

Libra supports JDK 8, scala versions 2.11.12, 2.12.11 and 2.13.2. The supported Scala.js version is 0.6.32

Add this to your build.sbt:

libraryDependencies += "com.github.to-ithaca" %% "libra" % "0.6.0"

For Scala.js, add this to your build.sbt:

libraryDependencies += "com.github.to-ithaca" %%% "libra" % "0.6.0"

TL;DR

Example usage:

import spire.implicits._
import libra._, libra.implicits._
(3.m + 2.m).show
(3.m * 2.m).show
(1.0.km.to[Metre] + 2.0.m + 3.0.mm.to[Metre]).show
(3.0.s.to[Millisecond] / 3.0.ms).show
3.m + 2.kg // This should fail
// error: These quantities can't be added!
// Most likely they have different dimensions.  If not, make sure that there's an implicit AdditiveSemigroup in scope.
// Left: libra.Quantity[Int,libra.Term[libra.Length,libra.Metre,libra.Fraction[Int(1),Int(1)]] :: shapeless.HNil]
// Right: libra.Quantity[Int,libra.Term[libra.Mass,libra.Kilogram,libra.Fraction[Int(1),Int(1)]] :: shapeless.HNil]
// 3.m + 2.kg // This should fail
// ^^^^^^^^^^

Why?

When we deal with numeric quantities, we often resort to Int, Double or Float types. These are incommunicative and error prone.

val distance = 3.0 // 3 m
// distance: Double = 3.0 // 3 m
val time = 2.0 // 2 s
// time: Double = 2.0 // 2 s
val speed = distance + time // Oh no!
// speed: Double = 5.0

There’s a mistake in our formula, but we won’t know without a decent set of tests.

Libra provides a Quantity which wraps base numeric types. It supports compile time dimensional analysis.

import spire.implicits._
import libra._, libra.implicits._
val distance = 3.0.m
val time = 2.0.s
distance + time
(distance / time).show // Yay!
// error: These quantities can't be added!
// Most likely they have different dimensions.  If not, make sure that there's an implicit AdditiveSemigroup in scope.
// Left: libra.Quantity[Double,libra.Term[libra.Length,libra.Metre,libra.Fraction[Int(1),Int(1)]] :: shapeless.HNil]
// Right: libra.Quantity[Double,libra.Term[libra.Time,libra.Second,libra.Fraction[Int(1),Int(1)]] :: shapeless.HNil]
// distance + time
// ^^^^^^^^^^^^^^^

Credits

In its incubation, Libra made heavy use of Typelevel Scala. It still makes use of shapeless, spire and singleton-ops. It wouldn’t be possible without these projects, their authors and contributors, so if you like Libra, please check them out.

Libra also uses:

Alternatives

If Libra isn’t quite your cup of tea, definitely take a look at Squants. It’s also a great library for dimensional analysis, with a slightly different scope.