Mizur, Kronos and Scope

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.

6 Likes