Wiki

Clone wiki

fling-units / Home

Fling Units provides a way to abstract away the specific units associated with a measurement, focusing instead on representing the fundamental quantity being measured. This allows for easy conversions between any unit types or measurement systems, but most importantly (and what sets Fling Units apart from other conversion libraries) it allows developers to encapsulate all measurement details into a single object with a strong type for compile-time verification and code agnosticism.

Why Fling Units?

We already mentioned compile-time verification and code agnosticism as two advantages of Fling Units, but what the heck do those things mean, and why should you care?

The Problem

Traditional unit conversion libraries do not include any type information: they expect developers to manage that themselves. Here is an example of a typical interaction with a generic unit conversion library:

double kilometers = Library.from(miles).to(kilometers, 20.0);

// or, in some cases:
double kilometers = 20.miles.toKilometers();

First, notice that the conversion accepts any number as input. The number takes meaning only because we passed it in to the conversion function, and from the context we can deduce that the 20.0 number must be a distance in miles. This is not type-safe, of course, and it is just as easy for a developer to pass along an incorrect variable here (so long as it is a numerical type), such as numberOfDoorsOnTheCar.

Second, notice that the conversion requires that the developer know the units used to provide the input value. If we wanted to keep track of the input value, we probably would name it something like distanceInMiles and we would be very careful to convert it to the correct unit prior to passing it in to any other functions, such as calculateFuelEfficiency. Does calculateFuelEfficiency accept a distance in miles or kilometers? To make sure we remember, we might name the parameter or rename the function in the hopes that developers will remember the correct units and parameter order:

double calculateFuelEfficiencyInMilesPerGallon(double distanceInMiles, double fuelInGallons) {...}

The Solution

Fling Units addresses these difficulties through abstraction and type checking while still providing the same conversion capability.

Using Fling Units to convert between measurement units:

double kilometers = miles(20.0).as(kilo.meters);

To see how the abstraction works, let's put together a similar fuel efficiency function signature, this time using Fling Units:

DerivedMeasurement<Distance, Volume> calculateFuelEfficiency(Distance distanceTraveled, Volume gasConsumed) {...}

Notice that there is no mention of units. In fact, the function definition itself doesn't need to know anything about them - it's just performing a division after all. Fling Units will do this work for you, so you don't even need this function - anyone can create a DerivedMeasurement and pass it around, then interpret it using whatever combination of base units they choose.

var fuelEfficiency = DerivedMeasurement<Distance, Volume>.divide(
    kilo.meters(120),
    liters(6.5)
);
print('Fuel efficiency is ${fuelEfficiency.as(miles, gallons)} miles per gallon');
print('Fuel efficiency is ${fuelEfficiency.as(inches, milli.liters)} inches per milliliter');

As an added bonus, the code is type-safe. If I need to know a fuel consumption rate and request a DerivedMeasurement<Distance, Volume>, any other unit or number would fail at compile time. For instance:

calculateFuelEfficiency(celcius(100.0));  // won't compile!

Other features

Encapsulating measurements this way has some other advantages, such as allowing easy comparisons and arithmetic operations, as well as intrinsic sorting:

Distance distanceToSeattleAndBack = distanceToSeattle * 2;

Volume amountOfFuelInTank = amountOfFuelInTank + amountOfFuelAdded;

pitstops = <Distance>[distanceToSeattle, distanceToNewYork, distanceToLasVegas];
pitstops.sort();

bool stillNearSeattle = distanceToSeattle < kilo.meters(50);

Updated