Hi, I work on a small company based on Lille (France), and we have launch our first Elixir project in production recently.
In the case of this project, we have been led to manipulate units of measurement from various systems.
To keep the code “relatively” readable (and avoid conversions in all directions), we have developed 2 small libraries:
Mizur
Mizur is a tool to simplify the management, conversion and mapping of units. The manipulation of measurement units should (at best) be typesafe.
The implementation is based on what could have been done with ghost types (in a language like Haskell or OCaml), and relies on pattern matching to structurally distinguish units of measurement from different systems.
Here is an example of use:
defmodule Distance do
use Mizur.System
type m
type cm = m / 100
type mm = m / 1000
type km = m * 1000
end
a = Distance.m(200)
b = Distance.cm(200)
result = Mizur.add(a, b)
assert result = Distance.m(202)
The library also offers infix operators (for comfort) and a notion of range
Even if the implementation is not perfect, Mizur has allowed us to reduce our base code and make it more readable!
Kronos
Although Elixir possesses a whole library gear for the manipulation of dates, we needed to do simple arithmetic operations on dates (and durations). To do this, we have created a library (based on Mizur) that offers a system for dates and times.
The library only handles Timestamps (because that’s all we needed) and has a very easy to use interface.
For example :
import Kronos
use Kronos.Infix # Same of Mizur.Infix
{:ok, t} = new({2010, 12, 20}, {0, 0, 0})
# You can use timestamp or DateTime.t as parameter for Kronos.new
r = t + ~t(2)day + ~t(3)hour + ~t(10)minute + ~t(13)second
IO.puts Kronos.to_string(r) # will print "2010-12-22 03:10:13Z"
Scope
Even if I know that Monkeypatching is to be avoided to the maximum, in the case of Mizur and Kronos, the overhead of arithmetic operators can actually improve readability. (Hence the existence of use Mizur.Infix
).
Scope is the exposure of two small macros to simulate OCaml’s let open module in
, allowing to open, locally to an expression, a module and possibly to overload some operators (or certain functions).
For example:
import Scope
# Overloading
overload [+: 2, -: 2], from: Kernel, with: Test
# Local opening
x = local System do
user_home <> " !"
end
# Or multiple module
y = local Elixir.{System, Path} do
absname(user_home())
end
# Or specifics function from a module (with a funky syntax)
z = local [user_home: 0, user_home!: 0] in System do
user_home <> " !"
end
Conclusion
Using Elixir is a real pleasure. I hope that these libraries will be useful to you.
A reflection to integrate the notion of “fraction” (eg km / year) is in progress for Mizur. Do not hesitate to contribute or make remarks.
Regards,
Xavier.