{"id":13561725,"url":"https://github.com/ianmackenzie/elm-units","last_synced_at":"2025-08-21T15:31:41.151Z","repository":{"id":43685640,"uuid":"141745455","full_name":"ianmackenzie/elm-units","owner":"ianmackenzie","description":"Simple, safe and convenient unit types and conversions for Elm","archived":false,"fork":false,"pushed_at":"2023-06-15T15:44:06.000Z","size":431,"stargazers_count":85,"open_issues_count":19,"forks_count":14,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-12-16T20:52:54.028Z","etag":null,"topics":["elm","hacktoberfest","units"],"latest_commit_sha":null,"homepage":"https://package.elm-lang.org/packages/ianmackenzie/elm-units/latest/","language":"Elm","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ianmackenzie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS"}},"created_at":"2018-07-20T18:32:14.000Z","updated_at":"2024-10-09T18:55:47.000Z","dependencies_parsed_at":"2024-01-14T03:34:08.863Z","dependency_job_id":"bd3ec454-50c0-4c7b-b802-9a8d3ef43df6","html_url":"https://github.com/ianmackenzie/elm-units","commit_stats":{"total_commits":488,"total_committers":11,"mean_commits":44.36363636363637,"dds":"0.15573770491803274","last_synced_commit":"664c72c12c9c6d1f165e36f77a4ed2cceb8cc236"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ianmackenzie%2Felm-units","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ianmackenzie%2Felm-units/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ianmackenzie%2Felm-units/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ianmackenzie%2Felm-units/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ianmackenzie","download_url":"https://codeload.github.com/ianmackenzie/elm-units/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230520390,"owners_count":18238948,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elm","hacktoberfest","units"],"created_at":"2024-08-01T13:01:00.382Z","updated_at":"2024-12-20T01:14:53.474Z","avatar_url":"https://github.com/ianmackenzie.png","language":"Elm","funding_links":[],"categories":["Elm"],"sub_categories":[],"readme":"# elm-units\n\n_Release notes for 2.0 are [here](https://github.com/ianmackenzie/elm-units/releases/tag/2.0.0)._\n\n`elm-units` is useful if you want to store, pass around, convert between,\ncompare, or do arithmetic on:\n\n- Durations (seconds, milliseconds, hours...)\n- Angles (degrees, radians, turns...)\n- Lengths (meters, feet, inches, miles, light years...)\n- Temperatures (Celsius, Fahrenheit, kelvins)\n- Pixels (whole or partial)\n- Speeds (pixels per second, miles per hour...) or any other rate of change\n- Any of the other built-in quantity types: areas, accelerations, masses,\n  forces, pressures, currents, voltages...\n- Or even values in your own custom units, such as 'number of tiles' in a\n  tile-based game\n\nIt is aimed especially at engineering/scientific/technical applications but is\ndesigned to be generic enough to work well for other fields such as games and\nfinance. The core of the package consists of a generic `Quantity` type and\nmany concrete types such as `Length`, `Angle`, `Duration`, `Temperature`, and\n`Speed`, which you can use to add some nice type safety to data types and\nfunction signatures:\n\n```elm\ntype alias Camera =\n    { fieldOfView : Angle\n    , shutterSpeed : Duration\n    , minimumOperatingTemperature : Temperature\n    }\n\ncanOperateAt : Temperature -\u003e Camera -\u003e Bool\ncanOperateAt temperature camera =\n    temperature\n        |\u003e Temperature.greaterThan\n            camera.minimumOperatingTemperature\n```\n\nYou can construct values of these types from any units you want, using provided\nfunctions such as:\n\n```elm\nLength.feet : Float -\u003e Length\nLength.meters :  Float -\u003e Length\nDuration.seconds : Float -\u003e Duration\nAngle.degrees : Float -\u003e Angle\nTemperature.degreesFahrenheit : Float -\u003e Temperature\n```\n\nYou can later convert back to plain numeric values, also in any units you want\n(which do not have to be the same units used when initially constructing the\nvalue!):\n\n```elm\nLength.inCentimeters : Length -\u003e Float\nLength.inMiles : Length -\u003e Float\nDuration.inHours : Duration -\u003e Float\nAngle.inRadians : Angle -\u003e Float\nTemperature.inDegreesCelsius : Temperature -\u003e Float\n```\n\nThis means that (among other things!) you can use these functions to do simple\nunit conversions:\n\n```elm\nDuration.hours 3 |\u003e Duration.inSeconds\n--\u003e 10800\n\nLength.feet 10 |\u003e Length.inMeters\n--\u003e 3.048\n\nSpeed.milesPerHour 60 |\u003e Speed.inMetersPerSecond\n--\u003e 26.8224\n\nTemperature.degreesCelsius 30\n    |\u003e Temperature.inDegreesFahrenheit\n--\u003e 86\n```\n\nAdditionally, types like `Length` are actually type aliases of the form\n`Quantity number units` (`Length` is `Quantity Float Meters`, for example,\nmeaning that it is internally stored as a number of meters), and there are\nmany generic functions which let you work directly with any kind of `Quantity`\nvalues:\n\n```elm\nLength.feet 3\n    |\u003e Quantity.lessThan (Length.meters 1)\n--\u003e True\n\nDuration.hours 2\n  |\u003e Quantity.plus (Duration.minutes 30)\n  |\u003e Duration.inSeconds\n--\u003e 9000\n\n-- Some functions can actually convert between units!\n-- Multiplying two Length values gives you an Area\nLength.centimeters 60\n    |\u003e Quantity.times\n        (Length.centimeters 80)\n--\u003e Area.squareMeters 0.48\n\nQuantity.sort\n    [ Angle.radians 1\n    , Angle.degrees 10\n    , Angle.turns 0.5\n    ]\n--\u003e [ Angle.degrees 10 , Angle.radians 1 , Angle.turns 0.5 ]\n```\n\nUltimately, what this does is let you pass around and manipulate `Length`,\n`Duration` or `Temperature` etc. values without having to worry about units.\nWhen you initially construct a `Length`, you need to specify what units you're\nusing, but once that is done you can:\n\n- Store the length inside a data structure\n- Pass it around between different functions\n- Compare it to other lengths\n- Add and subtract it to other lengths\n- Multiply it by another length to get an area, or divide by a duration to\n  get a speed\n\n...and much more, all without having to care about units at all. All\ncalculations will be done in an internally consistent way, and when you finally\nneed to actually display a value on screen or encode to JSON, you can extract\nthe final result in whatever units you want.\n\n## Table of contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Fundamentals](#fundamentals)\n  - [The `Quantity` type](#the-quantity-type)\n  - [Basic arithmetic and comparison](#basic-arithmetic-and-comparison)\n  - [Multiplication and division](#multiplication-and-division)\n  - [Argument order](#argument-order)\n  - [Custom functions](#custom-functions)\n  - [Custom units](#custom-units)\n  - [Understanding quantity types](#understanding-quantity-types)\n- [Getting help](#getting-help)\n- [API](#api)\n- [Climate action](#climate-action)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Installation\n\nAssuming you have [installed Elm](https://guide.elm-lang.org/install.html) and\nstarted a new project, you can install `elm-units` by running\n\n```\nelm install ianmackenzie/elm-units\n```\n\nin a command prompt inside your project directory.\n\n## Usage\n\n### Fundamentals\n\nTo take code that currently uses raw `Float` values and convert it to using\n`elm-units` types, there are three basic steps:\n\n- Wherever you store a `Float`, such as in your model or in a message, switch\n  to storing a `Duration` or `Angle` or `Temperature` etc. value instead.\n- Whenever you _have_ a `Float` (from an external package, JSON decoder etc.),\n  use a function such as `Duration.seconds`, `Angle.degrees` or\n  `Temperature.degreesFahrenheit` to turn it into a type-safe value.\n- Whenever you _need_ a `Float` (to pass to an external package, encode as\n  JSON etc.), use a function such as `Duration.inMillliseconds`,\n  `Angle.inRadians` or `Temperature.inDegreesCelsius` to extract the value in\n  whatever units you want.\n- Where you do math with `Float` values, switch to using `Quantity` functions\n  like `Quantity.plus` or `Quantity.greaterThan`. If this becomes impractical,\n  there are [other approaches](#custom-functions).\n\n### The `Quantity` type\n\nAll values produced by this package (with the exception of `Temperature`, which\nis a bit of a special case) are actually values of type `Quantity`, defined as\n\n```elm\ntype Quantity number units\n    = Quantity number\n```\n\nFor example, `Length` is defined as\n\n```elm\ntype alias Length =\n    Quantity Float Meters\n```\n\nThis means that a `Length` is internally stored as a `Float` number of `Meters`,\nbut the choice of internal units can mostly be treated as an implementation\ndetail.\n\nHaving a common `Quantity` type means that it is possible to define generic\narithmetic and comparison operations that work on any kind of quantity; read on!\n\n### Basic arithmetic and comparison\n\nYou can do basic math with `Quantity` values:\n\n```elm\n-- 6 feet 3 inches, converted to meters\nLength.feet 6\n    |\u003e Quantity.plus (Length.inches 3)\n    |\u003e Length.inMeters\n--\u003e 1.9050000000000002\n\nDuration.hours 1\n  |\u003e Quantity.minus (Duration.minutes 15)\n  |\u003e Duration.inMinutes\n--\u003e 45\n\n-- pi radians plus 45 degrees is 5/8 of a full turn\nQuantity.sum [ Angle.radians pi, Angle.degrees 45 ]\n    |\u003e Angle.inTurns\n--\u003e 0.625\n```\n\n`Quantity` values can be compared/sorted:\n\n```elm\nLength.meters 1 |\u003e Quantity.greaterThan (Length.feet 3)\n--\u003e True\n\nQuantity.compare (Length.meters 1) (Length.feet 3)\n--\u003e GT\n\nQuantity.max (Length.meters 1) (Length.feet 3)\n--\u003e Length.meters 1\n\nQuantity.maximum [ Length.meters 1, Length.feet 3 ]\n--\u003e Just (Length.meters 1)\n\nQuantity.sort [ Length.meters 1, Length.feet 3 ]\n--\u003e [ Length.feet 3, Length.meters 1 ]\n```\n\n### Multiplication and division\n\nThere are actually three different 'families' of multiplication and division\nfunctions in the `Quantity` module, used in different contexts:\n\n- `multiplyBy` and `divideBy` are used to multiply (scale) or divide a\n  `Quantity` by a plain `Int` or `Float`, with `twice` and `half` for the common\n  cases of multiplying or dividing by 2\n- `product`, `times`, `over` and `over_` are used to work with quantities that\n  are products of other quantities:\n  - multiply a `Length` by another `Length` to get an `Area`\n  - multiply an `Area` by a `Length` to get a `Volume`\n  - multiply a `Mass` by an `Acceleration` to get a `Force`\n  - divide a `Volume` by an `Area` to get a `Length`\n  - divide a `Force` by a `Mass` to get an `Acceleration`\n- `rate`, `per`, `at`, `at_` and `for` are used to work with rates of change:\n  - divide `Length` by `Duration` to get `Speed`\n  - multiply `Speed` by `Duration` to get `Length`\n  - divide `Length` by `Speed` to get `Duration`\n- And one bonus fourth function: `ratio`, used to divide two quantities with\n  the same units to get a plain `Float` value\n\nFor example, to calculate the area of a triangle:\n\n```elm\n-- Area of a triangle with base of 2 feet and\n-- height of 8 inches\nbase =\n    Length.feet 2\n\nheight =\n    Length.inches 8\n\nQuantity.half (Quantity.product base height)\n    |\u003e Area.inSquareInches\n--\u003e 96\n```\n\nComprehensive support is provided for working with rates of change:\n\n```elm\n-- How fast are we going if we travel 30 meters in\n-- 2 seconds?\nspeed =\n    Length.meters 30 |\u003e Quantity.per (Duration.seconds 2)\n\n-- How far do we go if we travel for 2 minutes\n-- at that speed?\nDuration.minutes 2 -- duration\n  |\u003e Quantity.at speed -- length per duration\n  |\u003e Length.inKilometers -- gives us a length!\n--\u003e 1.8\n\n-- How long will it take to travel 20 km\n-- if we're driving at 60 mph?\nLength.kilometers 20\n    |\u003e Quantity.at_ (Speed.milesPerHour 60)\n    |\u003e Duration.inMinutes\n--\u003e 12.427423844746679\n\n-- How fast is \"a mile a minute\", in kilometers per hour?\nLength.miles 1\n    |\u003e Quantity.per (Duration.minutes 1)\n    |\u003e Speed.inKilometersPerHour\n--\u003e 96.56064\n\n-- Reverse engineer the speed of light from defined\n-- lengths/durations (the speed of light is 'one light\n-- year per year')\nspeedOfLight =\n    Length.lightYears 1\n        |\u003e Quantity.per (Duration.julianYears 1)\n\nspeedOfLight |\u003e Speed.inMetersPerSecond\n--\u003e 299792458\n\n-- One astronomical unit is the (average) distance from the\n-- Sun to the Earth. Roughly how long does it take light to\n-- reach the Earth from the Sun?\nLength.astronomicalUnits 1\n    |\u003e Quantity.at_ speedOfLight\n    |\u003e Duration.inMinutes\n--\u003e 8.316746397269274\n```\n\nNote that the various functions above are not restricted to speed (length per\nunit time) - any units work:\n\n```elm\npixelDensity =\n    Pixels.pixels 96 |\u003e Quantity.per (Length.inches 1)\n\nLength.centimeters 3 -- length\n    |\u003e Quantity.at pixelDensity -- pixels per length\n    |\u003e Pixels.inPixels -- gives us pixels!\n--\u003e 113.38582677165354\n```\n\n### Argument order\n\nNote that several functions like `Quantity.minus` and `Quantity.lessThan` (and\ntheir `Temperature` equivalents) that mimic binary operators like `-` and `\u003c`\n\"take the second argument first\"; for example,\n\n```elm\nQuantity.lessThan x y\n```\n\nmeans `y \u003c x`, _not_ `x \u003c y`. This is done for a couple of reasons. First, so\nthat use with `|\u003e` works naturally; for example,\n\n```elm\nx |\u003e Quantity.lessThan y\n```\n\n_does_ mean `x \u003c y`. The 'reversed' argument order also means that things like\n\n```elm\nList.map (Quantity.minus x) [ a, b, c ]\n```\n\nwill work as expected - it will result in\n\n```elm\n[ a - x, b - x, c - x ]\n```\n\ninstead of\n\n```elm\n[ x - a, x - b, x - c ]\n```\n\nwhich is what you would get if `Quantity.minus` took arguments in the 'normal'\norder.\n\nThere are, however, several functions that take arguments in 'normal' order, for\nexample:\n\n- `Quantity.difference` (compare to `minus`)\n- `Quantity.product` (compare to `times`)\n- `Quantity.rate` (compare to `per`)\n- `Quantity.ratio`\n- `Quantity.compare`\n\nIn general the function names try to match how you would use them in English;\nyou would say \"the difference of `a` and `b`\" (and so `Quantity.difference a b`)\nbut \"`a` minus `b`\" (and so `a |\u003e Quantity.minus b`).\n\n### Custom Functions\n\nSome calculations cannot be expressed using the built-in `Quantity` functions.\nTake kinetic energy `E_k = 1/2 * m * v^2`, for example - the `elm-units` type\nsystem is not sophisticated enough to work out the units properly. Instead,\nyou'd need to create a custom function like\n\n```elm\nkineticEnergy : Mass -\u003e Speed -\u003e Energy\nkineticEnergy (Quantity m) (Quantity v) =\n    Quantity (0.5 * m * v^2)\n```\n\nIn the _implementation_ of `kineticEnergy`, you're working with raw `Float`\nvalues so you need to be careful to make sure the units actually do work out.\n(The values will be in [SI units][6] - meters, seconds etc.) Once the function\nhas been implemented, though, it can be used in a completely type-safe way -\ncallers can supply arguments using whatever units they have, and extract results\nin whatever units they want:\n\n```elm\nkineticEnergy (Mass.tonnes 1.5) (Speed.milesPerHour 60)\n    |\u003e Energy.inKilowattHours\n--\u003e 0.14988357119999998\n```\n\n### Custom Units\n\n`elm-units` defines many standard unit types, but you can easily define your\nown! See [CustomUnits][1] for an example.\n\n### Understanding quantity types\n\nThe same quantity type can often be expressed in multiple different ways. Take\nthe `Volume` type as an example. It is an alias for\n\n```elm\nQuantity Float CubicMeters\n```\n\nbut expanding the `CubicMeters` type alias, this is equivalent to\n\n```elm\nQuantity Float (Cubed Meters)\n```\n\nwhich expands further to\n\n```elm\nQuantity Float (Product (Product Meters Meters) Meters)\n```\n\nwhich could also be written as\n\n```elm\nQuantity Float (Product (Squared Meters) Meters)\n```\n\nor even\n\n```elm\nQuantity Float (Product SquareMeters Meters)\n```\n\nand you may see any one of these forms pop up in compiler error messages.\n\n## Getting Help\n\nFor general questions about using `elm-units`, try asking in the [Elm Slack][3]\nor posting on the [Elm Discourse forums][4] or the [Elm subreddit][5]. I'm\n**@ianmackenzie** on all three =)\n\n## API\n\n[Full API documentation][10] is available.\n\n## Climate action\n\nI would like for the projects I work on to be as helpful as possible in\naddressing the climate crisis. If\n\n- you are working on a project that helps address the climate crisis (clean\n  energy, public transit, reforestation, sustainable agriculture etc.) either as\n  an individual, as part of an non-profit organization or even as part of a\n  for-profit company, and\n- there is a new feature you would find helpful for that work (or a bug you need\n  fixed) in any of my open-source projects, then\n\nplease [open a new issue](https://github.com/ianmackenzie/elm-units/issues),\ndescribe briefly what you're working on and I will treat that issue as high\npriority.\n\n## Contributing\n\nYes please! One of the best ways to contribute is to add a module for a new\nquantity type; see [issue #6][7] for details. I'll add a proper CONTRIBUTING.md\nat some point, but some brief guidelines in the meantime:\n\n- Open a pull request by forking this repository, creating a new branch in\n  your fork, making all changes in that branch, then opening a pull request\n  from that branch.\n- Format code with [`elm-format`][8] 0.8.1.\n- Git commit messages should follow [the seven rules of a great Git commit\n  message][9], although I'm not strict about the 50 or 72 character rules.\n\n## License\n\n[BSD-3-Clause © Ian Mackenzie][2]\n\n[1]: https://github.com/ianmackenzie/elm-units/blob/master/doc/CustomUnits.md\n[2]: https://github.com/ianmackenzie/elm-units/blob/master/LICENSE\n[3]: http://elmlang.herokuapp.com/\n[4]: https://discourse.elm-lang.org/\n[5]: https://www.reddit.com/r/elm/\n[6]: https://en.wikipedia.org/wiki/International_System_of_Units\n[7]: https://github.com/ianmackenzie/elm-units/issues/6\n[8]: https://github.com/avh4/elm-format\n[9]: https://chris.beams.io/posts/git-commit/#seven-rules\n[10]: https://package.elm-lang.org/packages/ianmackenzie/elm-units/latest\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fianmackenzie%2Felm-units","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fianmackenzie%2Felm-units","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fianmackenzie%2Felm-units/lists"}