Value-level dimensions
module Dimensions.ValueLevel
( Dim(..)
, mul
, div
, length
, mass
, time
, current
, temperature
, substance
, luminosity
, one
) where
import Prelude hiding (length, div)
From the introduction, two things became apparanent:
- Given the unit of a quantity, its dimension is known implicitly.
- If we only work with SI-units, there is a one-to-one correspondence between dimensions and units.
We’ll use these facts when implementing dimensions. More precisely, “length” and “metre” will be interchangable, and so on.
A dimension can be seen as a product of the base dimensions, with an individual exponent on each base dimension. Because the 7 base dimensions are known in advance, we can design our data type using this fact.
data Dim = Dim Integer -- Length
Integer -- Mass
Integer -- Time
Integer -- Current
Integer -- Temperature
Integer -- Substance
Integer -- Luminosity
deriving (Eq)
Each field denotes the exponent for the corresponding base dimension. If the exponent is 0
, the base dimension is not part of the dimension. Some examples should clarify.
length = Dim 1 0 0 0 0 0 0
mass = Dim 0 1 0 0 0 0 0
time = Dim 0 0 1 0 0 0 0
current = Dim 0 0 0 1 0 0 0
temperature = Dim 0 0 0 0 1 0 0
substance = Dim 0 0 0 0 0 1 0
luminosity = Dim 0 0 0 0 0 0 1
Velocity is \(m/s\) or equivalently \(m^1*s^{ -1 }\). This explains why the exponents are as above.
Noticed how we used “m” (for metre) for implicitly refering to the dimension “length”? It’s quite natural to work this way.
Exercise Create values for acceleration, area and charge.
Multiplication and division
Dimensions can be multiplied and divided. Velocity is, as we just saw, a division between length and time. Multiplication and division of dimensions are performed as if they were regular numbers, or variables holding numbers, and hence they follow the power laws. That is, to multiply, the exponents of the two numbers are added, and to divide, the exponents are subtracted.
mul :: Dim -> Dim -> Dim
(Dim le1 ma1 ti1 cu1 te1 su1 lu1) `mul` (Dim le2 ma2 ti2 cu2 te2 su2 lu2) =
Dim (le1+le2) (ma1+ma2) (ti1+ti2) (cu1+cu2) (te1+te2) (su1+su2) (lu1+lu2)
Exercise Implement a function for dividing two dimensions.
Solution
It’s now possible to construct dimensions in the following fashion.
velocity' = length `div` time
area' = length `mul` length
force = mass `mul` acceleration
momentum = force `mul` time
A dimension we so far haven’t mentioned is the scalar, which shows up when working with, for example, coefficients of friction. It’s dimensionless because it arises from division of two equal dimensions. The case of coefficients of friction looks like
\[\begin{align} F_{friction} = \mu * F_{normal} \iff \mu = \frac{F_{friction}}{F_{normal}} \end{align}\]Exercise Create two values, which represent the scalar. They should of course have the same value, but be created in two different ways. One by writing the exponents explicitly. One by dividing two equal dimensions.
Pretty-printer
The purpose of value-level dimensions is to be able to print ’em nicely. The pretty printer should be function with the type
meaning it shows a dimension as a string. But how to actually implement this is not the interesting part of this tutorial. Hence we skip it.
We use showDim
to make Dim
an instance of Show
Now dimensions are printed in a quite pretty way in GHCi
Note that the SI-unit of the dimensions is used for printing.
The result from this section is the ability to multiply, divide and print dimensions. The next step is to test these operations and see if they actually work.