unit_fun alternatives and similar packages
Based on the "Text and Numbers" category.
Alternatively, view unit_fun alternatives based on common mentions on social networks and blogs.
-
money
Elixir library for working with Money safer, easier, and fun... Is an interpretation of the Fowler's Money pattern in fun.prog. -
nanoid
Elixir port of NanoID, a secure and URL-friendly unique ID generator. https://hex.pm/packages/nanoid -
secure_random
Convenience library for random base64 strings modeled after my love for Ruby's SecureRandom -
chinese_translation
An elixir module to translate simplified Chinese to traditional Chinese, and vice versa, based on wikipedia data -
inet_cidr
CIDR library for Elixir that is compatible with Erlang's :inet and supports both IPv4 and IPv6 -
Ex_Cldr_Units
Unit formatting (volume, area, length, ...) functions for the Common Locale Data Repository (CLDR) -
minigen
Pure random data generation library, appropriate for realistic simulations in the Erlang ecosystem
CodeRabbit: AI Code Reviews for Developers

Do you think we are missing an alternative of unit_fun or a related project?
README
UnitFun
Attempt to add units to numbers in elixir to give some added type saftey when dealing with numeric quantities.
Why?
One good example: pounds(dollars) should never be accidentally added to pence(cents) without conversion. Both are numeric. Wrapping the numeric data in a tuple with unit information seems like a good idea. This library gives a neat way of expressing this.
Example - Basic
First define some units:
defmodule Pounds do
use UnitFun.Unit
end
defmodule Pence do
use UnitFun.Unit
end
Then do something with them:
use UnitFun.MathsOperators
import UnitFun.UnitTypes
item_cost = 5 <~ Pounds # UnitFun.with_units(5, Pounds)
item_tax = 100 <~ Pence # UnitFun.with_units(100, Pence)
# The following will throw an error as the units mismatch:
item_cost + item_tax # UnitFun.add(item_cost, item_tax)
Example - Conversions
Conversions can be defined:
defimpl UnitFun.Convertor, for: Pence do
def convert(_, Pounds, value), do: (value * 100)
end
defimpl UnitFun.Convertor, for: Pounds do
#Note: please don't actually do a divison for any financial maths
# You're going to lose data and have a bad time.
def convert(_, Pence, value), do: (value / 100)
end
And now the following:
# returns: %UnitFun.Value{value: 6, units: Pounds}
total = item_cost + item_tax # UnitFun.add(item_cost, item_tax)
# returns: %UnitFun.Value{value: 600, units: Pence}
total_in_pence = total <~ Pence # UnitFun.with_units(total, Pence)
Example - Assertion
Errors can be raised if units aren't what they are expected to be:
UnitFun.assert_units(total_money, Miles)
Example - Composite units
New units can also be composed by multiplying existing units together:
use UnitFun.MathsOperators
import UnitFun.UnitTypes
km_squared = Kilometers * Kilometers # UnitFun.multiply(Kilometers, Kilometers)
These newly defined units can then be used as with all previous examples
edge = 4 <~ Kilometers # UnitFun.with_units(4, Kilometers)
area = edge * edge # UnitFun.multiply(edge, edge)
expected_area = 16 <~ km_squared # UnitFun.with_units(16, km_squared)
assert area == expected_area # UnitFun.equals(area, expected_area)
Dividing/multiplying by united types returns values with new types so correctness can be asserted on.
miles_per_hour = Miles / Hours # UnitFun.divide(Miles, Hours)
speed = 40 <~ miles_per_hour # UnitFun.with_units(40, miles_per_hour)
time_spent_travelling = 2 <~ Hours # UnitFun.with_units(2, hours)
#the distance will be in Miles as the hours cancel out
distance_travelled_in_two_hours = time_spent_travelling * speed # UnitFun.multiply(time_spent_travelling, speed)
assert distance_travelled_in_two_hours == 80 <~ Miles # UnitFun.with_units(80, Miles)
Example - Composite unit conversions
If there's a single unit way of representing some composite units this conversion can also be defined (N.B. there's currently no way of defining a conversion from a simple unit to a composite one):
defimpl UnitFun.Convertor, for: UnitFun.ConvertorComplexTest.Pascals do
alias UnitFun.ConvertorComplexTest.Meters
alias UnitFun.ConvertorComplexTest.Newtons
alias UnitFun.Units.CompositeUnit
def convert(_, %CompositeUnit{numerators: [%Newtons{}], denominators: [%Meters{}]}, value) do
value
end
end
Example - Custom mathematic functions.
All the maths is controlled by protocols. So for example if you decided pence should only be handled as integers (so rounding isn't an issue) the following protocol could be defined:
defimpl UnitFun.Maths.AddSubtractMaths, for: UnitFun.ExampleTest.Pence do
def add(_, left, right) when is_integer(left) and is_integer(right) do
left + right
end
def subtract(_, left, right) when is_integer(left) and is_integer(right) do
left - right
end
end
Now any addition using non integer quantities will raise a FunctionClauseError. For convinience if nothing is defined then the kernel +-/* are used.
Example - Facts about units
It's possible to define units with facts that must always hold true. This is handled by defining a list of functions that return true or false.
defmodule UnitFun.Examples.PositiveUnit do
@moduledoc false
use UnitFun.Unit
defp greater_than_zero(x), do: x >= 0
facts [
&greater_than_zero/1
]
end
Now whenever a PositiveUnit
value is constructed the greater_than_zero callback will be executed.
If this returns False then an InvalidValueError will be raised.