GithubHelp home page GithubHelp logo

timjb / quantities Goto Github PK

View Code? Open in Web Editor NEW
157.0 12.0 11.0 290 KB

Type-safe physical computations and unit conversions in Idris โš– ๐ŸŒก โฒ ๐Ÿ”‹ ๐Ÿ“

Home Page: http://timbaumann.info/quantities/docs

License: MIT License

Idris 98.58% Shell 0.58% Nix 0.83%
type-safety units-of-measure unit-conversion

quantities's Introduction

Quantities Build Status

Quantities is a library for type-safe physical computations and unit conversions in Idris.

New Cuyama

(Population Explosion! by 7-how-7 โ€“ sign first seen on Andrew Kennedy's Units-of-Measure page)

I'm collecting links on types and units of measures in the wiki. If you know an interesting project, paper etc. you're invited to add it to the list!

Installation

Copy this package and run

$ idris --install quantities.ipkg

To use it in your program, run Idris with

$ idris -p quantities yourprogram.idr

Compatibility: Tested with Idris 1.3.1

Documentation

Quantities

Quantities are physical properties that you can measure. They include length, speed, pressure, electric resistance, etc. We can multiply and divide quantities to form new quantities:

Area : Quantity
Area = Length <*> Length

Speed : Quantity
Speed = Length </> Time

Volume : Quantity
Volume = Length ^ 3

Frequency : Quantity
Frequency = Time ^ (-1)

Above we defined the quantities Area, Speed, Volume and Frequency in terms of Length and Time. By convention, we write quantities with capital letters.

Dimensions

Of course, we can't derive all quantities from existing quantities, but have to start with some base quantities. The SI system of units defines Length, Mass, Time, ElectricCurrent, Temperature, AmountOfSubstance and LuminousIntensity as base quantities. We can declare them like this:

Length : Dimension
Length = MkDimension "Length"

Time : Dimension
Time = MkDimension "Time"

Happiness : Dimension
Happiness = MkDimension "Happiness"

The Quantity data type is now defined as the free abelian group over the Dimension data type. There is a function, dimensionToQuantity : Dimension -> Quantity, which implicitly converts dimensions to quantities.

Units

A unit represents a specific amount of a quantity. For example, we have

Centimetre : Unit Length
Second : Unit Time
Ampere : Unit ElectricCurrent
Newton : Unit Force

Notice that units are indexed by the quantity they represent. Like with quantities, we can multiply and devide units to derive new units. But there is a catch: when we multiply two units, the resulting unit represents the product of their respective quantities. For example, when we multiply the unit Centimetre with itself, we get a unit for area, since Area = Length <*> Length. Therefore, we have the functions

(<**>) : {q : Quantity} -> {r : Quantity} -> Unit q -> Unit r -> Unit (q <*> r)
(<//>) : {q : Quantity} -> {r : Quantity} -> Unit q -> Unit r -> Unit (q </> r)
(^^)   : {q : Quantity} -> Unit r -> (i : Integer) -> Unit (q ^ i)

For example:

SquareCentimetre : Unit Area
SquareCentimetre = Centimetre <**> Centimetre -- = Centimetre ^^ 2

MetrePerSecond : Unit Speed
MetrePerSecond = Meter <//> Second

CubicCentimetre : Unit Volume
CubicCentimetre = Centimetre ^^ 3

Newton : Unit ((Length <*> Mass) </> (Time ^ 2))
Newton = (Metre <**> Kilogram) <//> (Second ^^ 2)

Elementary Units

We have to start somewhere by defining some base units:

Metre : ElemUnit Length
Metre = MkElemUnit "m" 1

Second : ElemUnit Time
Second = MkElemUnit "s" 1

Candela : ElemUnit LuminousIntensity
Candela = MkElemUnit "cd" 1

-- the quantity of happiness that a one kilogram beagle puppy whose body temperature is 310 kelvins produces when held in skin contact for one second
Puppy : ElemUnit Happiness
Puppy = MkElemUnit "puppy" 1

These are called elementary units. The number at the end of MkElemUnit is the conversion rate to the base unit of the quantity. Since Metre, Candela and Puppy are the base units themselves, the conversion rate for them is 1. Which unit you consider as a base unit for a dimension isn't important as long as you stay consistent with your choices.

Elementary units are not just a way to bootstrap the system of units; they can also be used to define other units, with some syntax sugar:

Mile : ElemUnit Length
Mile = < one "mile" equals 1609.344 Metre >

-- Speed of light
C_0 : ElemUnit Speed
C_0 = < one "c_0" equals 299792458 (Metre <//> Second) >

-- If you're like me ...
Kitten : ElemUnit Happiness
Kitten = < one "kitten" equals 1.5 Puppy >

Units are defined as the free abelian group over elementary units, with the addition that we keep track of the quantities that are represented by the units.

Elementary units are implicitly converted to units by the function

elemUnitToUnit : {q : Quantity} -> ElemUnit q -> Unit q

Measurements

Measurements are values tagged with a unit.

data Measurement : {q : Quantity} -> Unit q -> Type -> Type where
  (=|) : a -> (u : Unit q) -> Measurement u a

Since Measurement is a bit long, there is a shorthand form: u :| a is the same as Measurement u a. For measurements with float values there is an even shorter alias:

F : Unit q -> Type
F u = Measurement u Float

For example:

distanceToMoon : F Metre
distanceToMoon = 384400000.0 =| Metre

Converting between units

Sometimes, a conversion isn't necessary. For example, the unit Newton is definitionally equal to (Metre <**> Kilogram) <//> (Second ^^ 2), so you won't have to convert between these. But generally, you will need a conversion function.

distanceToMoonInMiles : F miles
distanceToMoonInMiles = convertTo Mile distanceToMoon

-- According to Wikipedia
DogYear : ElemUnit Time
DogYear = < one "dy" equals 52 Day >

myAgeInDogYears : F DogYear
myAgeInDogYears = (21 =| Year) `as` DogYear

Since the target unit in the first example is clear from the context, we could write convert instead of convertTo Mile. For reference, the conversion functions used above are

convertTo : {from : Unit q} -> (to : Unit q) -> F from -> F to
convert   : {from : Unit q} -> {to : Unit q} -> F from -> F to
as        : {from : Unit q} -> F from -> (to : Unit q) -> F to

Calculations with measurements

Let's say I've lifted a 5 kg weight from ground to a height of 2 metre in 0.8 seconds. What's the average power of this action?

weight : F Kilogram
weight = 2 =| Kilogram

height : F Metre
height = 2 =| Metre

duration : F Second
duration = 0.8 =| Second

g_0 : F (Metre <//> (Second ^^ 2))
g_0 = 9.80665 =| (Metre <//> (Second ^^ 2))

averagePower : F Watt
averagePower = convert $ (weight |*| height |*| g_0) |/| duration
-- = 49.033 Watt

This example shows how to multiply measurements using the functions

(|*|) : Num a => {u : Unit q} -> {v : Unit r} -> u :| a -> v :| a -> (u <**> v) :| a
(|/|) : {u : Unit q} -> {v : Unit r} -> F u -> F v -> F (u <//> v)
(|^|) : {u : Unit q} -> F u -> (i : Integer) -> F (u ^^ i)

We can even use these functions to multiply measurements with scalar values:

energyConversionEfficiency : F One
energyConversionEfficiency = 0.88 =| One

batteryCapacity : F (Watt <**> Hour)
batteryCapacity = 85000 =| (Watt <**> Hour)

usedEnergy : F (Watt <**> Hour)
usedEnergy = convert $ energyConversionEfficiency |*| batteryCapacity

We can add and subtract measurements, too, but only if they have the same unit:

(<+>) : Num a => Measurement u a -> Measurement u a -> Measurement u a
(<->) : Num a => Measurement u a -> Measurement u a -> Measurement u a

For example:

eatChocolateCake : F Puppy -> F Puppy
eatChocolateCake x = x <+> (2 =| Puppy)

Predefined quantities and units

The library comes with many quantities and units predefined.

From the International System of Units (SI):

These four modules are reexported by the main module Quantities.

Other quantities and units:

Metric Prefixes

All standard SI prefixes are supported. For example:

import Quantities

microscopeResolution : F (nano Metre)
microscopeResolution = 180 =| (nano Metre)

performance : F (mega Watt)
performance = 3.1 =| (mega Watt)

Example

A simple example that demonstrates how one could use quantities to implement simple movement with gravity in a game.

module Game

import Quantities
import Quantities.Screen

ScreenSpeed : Quantity
ScreenSpeed = ScreenLength </> Time

Pxs : Unit ScreenSpeed
Pxs = Pixel <//> Second

record PlayerState where
  constructor MkPlayerState
  xSpeed : F Pxs
  ySpeed : F Pxs
  xPos   : F Px
  yPos   : F Px

gravity : Quantities.Core.F (Pxs <//> Second)
gravity = -800 =| (Pxs <//> Second)

-- Update player position and speed after a given duration
updatePlayerState : F Second -> PlayerState -> PlayerState
updatePlayerState dt (MkPlayerState xs ys xp yp) =
  let newYPos = yp <+> ys |*| dt
  in if newYPos <= (0 =| Px)
       then MkPlayerState (0 =| Pxs) (0 =| Pxs) xp (0 =| Px)
       else MkPlayerState xs (ys <+> gravity |*| dt)
                          (xp <+> xs |*| dt) newYPos

Contributing

Feedback and pull requests adding code and units are welcome!

quantities's People

Contributors

ethansr avatar iblech avatar matheus23 avatar timjb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quantities's Issues

Issue with quantities in README.md

This sentence appears in the readme:

By convention, we write quantities with a capital letters.

It mixes Singular and Plural quantities.

The types should be made to line up in the Singular...

By convention, we write quantities with a capital letter.

...or in the Plural:

By convention, we write quantities with capital letters.

Doesn't compile on Idris 1.0

In Core.idr, the dependent function ElemUnit' doesn't seem to count as a type constructor. Attempting to compile gives

./Quantities/Core.idr line 65 col 15:
(q : FreeAbGrp Dimension ** ElemUnit q)  cannot be a parameter of Prelude.Interfaces.Eq
(Implementation arguments must be type or data constructors)

I suspect this issue is related to idris-lang/Idris-dev#3727

Float deprecation warnings

Type checking ./Quantities/Power.idr
./Quantities/Power.idr:57:10:Use of deprecated name Float
./Quantities/Power.idr:57:10:Use of deprecated name Float
./Quantities/Power.idr:57:10:Use of deprecated name Float
./Quantities/Power.idr:58:9:Use of deprecated name Float
./Quantities/Power.idr:58:9:Use of deprecated name Float
./Quantities/Power.idr:58:9:Use of deprecated name Float
./Quantities/Power.idr:58:9:Use of deprecated name Float
./Quantities/Power.idr:59:9:Use of deprecated name Float
./Quantities/Power.idr:59:9:Use of deprecated name Float
./Quantities/Power.idr:59:9:Use of deprecated name Float
./Quantities/Power.idr:60:5-7:Use of deprecated name Float
./Quantities/Power.idr:60:5-7:Use of deprecated name Float
./Quantities/Power.idr:60:5-7:Use of deprecated name Float
./Quantities/Power.idr:60:5-7:Use of deprecated name Float
./Quantities/Power.idr:59:38:Use of deprecated name Float
./Quantities/Power.idr:59:38:Use of deprecated name Float
./Quantities/Power.idr:59:38:Use of deprecated name Float
./Quantities/Power.idr:59:38:Use of deprecated name Float
./Quantities/Power.idr:59:9:Use of deprecated name Float
./Quantities/Power.idr:59:9:Use of deprecated name Float
Type checking ./Quantities/FreeAbelianGroup.idr
Type checking ./Quantities/Core.idr
./Quantities/Core.idr:52:8:Use of deprecated name Float
./Quantities/Core.idr:52:8:Use of deprecated name Float
./Quantities/Core.idr:52:8:Use of deprecated name Float
./Quantities/Core.idr:52:8:Use of deprecated name Float
./Quantities/Core.idr:52:8:Use of deprecated name Float
./Quantities/Core.idr:52:8:Use of deprecated name Float
./Quantities/Core.idr:52:8:Use of deprecated name Float
./Quantities/Core.idr:52:8:Use of deprecated name Float
./Quantities/Core.idr:52:8:Use of deprecated name Float
./Quantities/Core.idr:52:8:Use of deprecated name Float
./Quantities/Core.idr:78:17:Use of deprecated name Float
./Quantities/Core.idr:78:17:Use of deprecated name Float
./Quantities/Core.idr:79:17:Use of deprecated name Float
./Quantities/Core.idr:79:17:Use of deprecated name Float
./Quantities/Core.idr:133:22:Use of deprecated name Float
./Quantities/Core.idr:133:22:Use of deprecated name Float
./Quantities/Core.idr:134:22:Use of deprecated name Float
./Quantities/Core.idr:139:20:Use of deprecated name Float
./Quantities/Core.idr:139:20:Use of deprecated name Float
./Quantities/Core.idr:140:20:Use of deprecated name Float
./Quantities/Core.idr:140:20:Use of deprecated name Float
./Quantities/Core.idr:140:20:Use of deprecated name Float
./Quantities/Core.idr:140:20:Use of deprecated name Float
./Quantities/Core.idr:285:3:Use of deprecated name Float
./Quantities/Core.idr:321:18:Use of deprecated name Float
./Quantities/Core.idr:322:18:Use of deprecated name Float
Type checking ./Quantities/SIPrefixes.idr
Type checking ./Quantities/SIBaseQuantities.idr
Type checking ./Quantities/SIBaseUnits.idr
Type checking ./Quantities/SIDerivedQuantities.idr
Type checking ./Quantities/SIDerivedUnits.idr
Type checking ./Quantities.idr
Type checking ./Quantities/NonSIUnits.idr
./Quantities/NonSIUnits.idr:17:8:Use of deprecated name Float
./Quantities/NonSIUnits.idr:22:6:Use of deprecated name Float
./Quantities/NonSIUnits.idr:27:5:Use of deprecated name Float
./Quantities/NonSIUnits.idr:32:6:Use of deprecated name Float
./Quantities/NonSIUnits.idr:49:7:Use of deprecated name Float
./Quantities/NonSIUnits.idr:69:18:Use of deprecated name Float
./Quantities/NonSIUnits.idr:92:5:Use of deprecated name Float
./Quantities/NonSIUnits.idr:95:9:Use of deprecated name Float
./Quantities/NonSIUnits.idr:100:6:Use of deprecated name Float
./Quantities/NonSIUnits.idr:105:7:Use of deprecated name Float
./Quantities/NonSIUnits.idr:112:5:Use of deprecated name Float
./Quantities/NonSIUnits.idr:182:18:Use of deprecated name Float
./Quantities/NonSIUnits.idr:187:14:Use of deprecated name Float
./Quantities/NonSIUnits.idr:192:19:Use of deprecated name Float
./Quantities/NonSIUnits.idr:197:13:Use of deprecated name Float
./Quantities/NonSIUnits.idr:202:13:Use of deprecated name Float
./Quantities/NonSIUnits.idr:207:11:Use of deprecated name Float
./Quantities/NonSIUnits.idr:212:10:Use of deprecated name Float
./Quantities/NonSIUnits.idr:217:11:Use of deprecated name Float
./Quantities/NonSIUnits.idr:222:11:Use of deprecated name Float
Type checking ./Quantities/ImperialUnits.idr
./Quantities/ImperialUnits.idr:30:6:Use of deprecated name Float
./Quantities/ImperialUnits.idr:35:6:Use of deprecated name Float
./Quantities/ImperialUnits.idr:40:7:Use of deprecated name Float
./Quantities/ImperialUnits.idr:45:9:Use of deprecated name Float
./Quantities/ImperialUnits.idr:50:6:Use of deprecated name Float
./Quantities/ImperialUnits.idr:55:8:Use of deprecated name Float
./Quantities/ImperialUnits.idr:67:7:Use of deprecated name Float
./Quantities/ImperialUnits.idr:70:14:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:86:6:Use of deprecated name Float
./Quantities/ImperialUnits.idr:94:7:Use of deprecated name Float
./Quantities/ImperialUnits.idr:97:6:Use of deprecated name Float
./Quantities/ImperialUnits.idr:100:6:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:111:6:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:116:6:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:121:7:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:126:8:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:138:14:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:143:13:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:171:7:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:176:9:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:181:15:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:186:5:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:192:11:
Use of deprecated name Float
./Quantities/ImperialUnits.idr:200:22:
Use of deprecated name Float
Type checking ./Quantities/Information.idr
./Quantities/Information.idr:12:6:Use of deprecated name Float
./Quantities/Information.idr:19:13:Use of deprecated name Float
Type checking ./Quantities/Screen.idr
./Quantities/Screen.idr:25:20:Use of deprecated name Float
./Quantities/Screen.idr:32:14:Use of deprecated name Float

Unable to build with latest Idris-dev

Trying to build the latest version of the quantities library (8fbe45f) using Idris 0.9.19.1 (b49893e), I get the following errors:

~/S/quantities git:master โฏโฏโฏ idris --install quantities.ipkg
Type checking ./Quantities/Core.idr
./Quantities/Core.idr:104:5:
When checking right hand side of one with expected type
        Unit scalar

Type mismatch between
        Unit (lift getWitness neutral) (Type of MkUnit 0 neutral)
and
        Unit scalar (Expected type)

Specifically:
        Type mismatch between
                lift getWitness neutral
        and
                scalar
./Quantities/Core.idr:108:5:
When checking right hand side of ten with expected type
        Unit scalar

Type mismatch between
        Unit (lift getWitness neutral) (Type of MkUnit 1 neutral)
and
        Unit scalar (Expected type)

Specifically:
        Type mismatch between
                lift getWitness neutral
        and
                scalar
./Quantities/Core.idr:112:9:
When checking right hand side of percent with expected type
        Unit scalar

Type mismatch between
        Unit (lift getWitness neutral) (Type of MkUnit (negate 2)
                                                       neutral)
and
        Unit scalar (Expected type)

Specifically:
        Type mismatch between
                lift getWitness neutral
        and
                scalar
./Quantities/Core.idr:116:10:
When checking right hand side of promille with expected type
        Unit scalar

Type mismatch between
        Unit (lift getWitness neutral) (Type of MkUnit (negate 3)
                                                       neutral)
and
        Unit scalar (Expected type)

Specifically:
        Type mismatch between
                lift getWitness neutral
        and
                scalar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.