Assume such measures are defined:
[<Measure>] type kilogram
[<Measure>] type kg = kilogram
[<Measure>] type s
When I define a binding like this:
let x = 1.<kg / kilogram> // Type of x: float
F# correctly simplifies type of x from float<kg/kilogram> to float.
But when I add another unit like this:
let y = 1.<kg s / kilogram> // Type of y: float<kg s / kilogram>
Instead of simplfying type of y to float<s>, it shows float<kg s / kilogram>
Why doesn't F# simplify it in this case? Am I doing something wrong?
As far as I can see based on a couple of experiments, the only place where the compiler uses the fact that kg = kilogram in the simplification process is when the final type is unit-less, i.e. float<1> type. In all other cases I tried, it keeps both kg and kilogram in the unit type.
I suspect the compiler still knows that the type of 1.<kg/kilogram> is kg/kilogram, but because this is equivalent to 1, it does not display it - it shows float rather than float<1> or float<kg/kilogram>.
It is worht noting that the simplificiation happens when it is actually needed - if you try comparing a value 1.<kg s / kilogram> with a value 1.<s / 1>, this is well-typed:
let y = 1.<kg s / kilogram>
y = 1.<s/1>
I do not have a solid evidence (like a link to the specification), but I think the compiler only does the simplification when it actually has to, but otherwise keeps the unit annotations as you write them.
Related
I realize that you can express relationships with dimensions of units, like
[<Measure>] type cc = cm^3
and perform meaningful calculations later.
Given some unit of measure type,
[<Measure>] type m
Is it possible to define a unit in a relationship with a quantity of another unit? For example,
// doesn't compile
[<Measure>] type mm = 0.001<m>
// later
let length = 500.0<mm>
let length2 = 0.5<m>
printfn "%A" (length = length2) // prints true
In short: no.
Units of measure are annotations on primitives. Period. As you probably know, they will be deleted during compilation.
So here's their fundamental limitation: you cannot attach any kind of functionality to them, because they will all turn into plain old floats.
The compiler will check that your expressions are dimensionally valid, but (for now) it does not automatically generate or insert any sort of 'default' type-conversion functions.
You must write and use those functions yourself, and the best you can do is to make them as straightforward as possible.
Here's how I'd organise your example:
[<Measure>] type mm
[<Measure>] type mt
// first, I like to define basic functions to quickly annotate dimensionless values
let mm = (*) 1.0<mm>
let mt = (*) 1.0<mt>
// we define a constant conversion
let MmPerMt = 1000.0<mm/mt>
// (though nothing forbids us from defining any conversion we want, and the compiler cannot privilege one over another)
let INeverPaidAttentionInGradeSchool = 12345<mm/mt>
// for ease of use, we bake the conversion constant into functions
let MtToMm = (*) MmPerMt
// usage
let someUserInputInMeters = "12414.23"
let desiredValueInMillimeters = someUserInputInMeters
|> float
|> mt
|> MtToMm
I've recently started playing around with Units Of Measure in F# and thought I could write a very simple example that calculates a gross amount given a VAT rate and a net total.
For instance:
The net amount might equal 600.00
The VAT rate would be 20%
Which should give a Gross amount of 720.00
I have the following types
[<Measure>] type net
[<Measure>] type vatRate
[<Measure>] type vatValue = net * vatRate
[<Measure>] type gross
And the following functions
let calculateVat (netValue : float<net>) (vat : float<vatRate>) = netValue * vat
let calculateGross (netValue : float<net>) (vat : float<vatValue>) = netValue + vat
With the following tests:
let calcVatTest = calculateVat 600.00<net> 0.2<vatRate> = 120.00<vatValue>
let calcGrossTest = calculateGross 600.00<net> 120.00<vatValue> = 720.00<gross>
The problem I'm having is that I can't get the correct syntax for the calculateGross function and I'm getting a compilation error:
"The unit of measure 'vatValue' does not match the unit of measure 'net'"
It seems as though I need to define gross similar to the following:
[<Measure>] type gross = net + vatValue
But the compiler doesn't like the +
Any ideas how I might achieve this?
Thanks
Only operators *, /, and ^ are supported in measure expressions—although - may be used to construct a negative exponent. Logically, this makes sense because in order to use dimensional analysis, the compiler has to consider each factor to consist of a scalar and a single units or a product of units.
Honestly, this doesn't seem to be a good use for units of measure. It looks like it just complicates your code without providing too much more expressiveness.
Further reading
Units of Measure (F#)
I know you didn't ask but this is too big a point to pass up and this is too much to put into a comment. I think probably you don't want the unit of measure for gross, vatRate etc because I'd expect gross, net and so forth to be in terms of currency.
Something more like this (assuming the VAT is a percent of a European currency):
[<Measure>] type euro
[<Measure>] type percent
let gross = 100.0<euro>
let vatRate = 5.0<percent>
I mean to say that I think you've gotten hold of the wrong way to use units of measure. Gross isn't a unit of measure--it's a number; likewise vatRate.
In your sample, the problem is that you are trying to add two things with different units (price without VAT and VAT value) - that's not allowed by the static typing - you can only add things of the same unit (which is part of the principles behind units of measure - there is not much you can do about this).
I think that the most natural solution (that, however, does not give you as strong safety guarantees) would be to make the VAT rate dimensionless number.
In general (when thinking about the physical meaning), rates are examples of number that does not have a unit - rate is generally calculated as X<unit> / Y<unit> for some numbers X and Y of the same unit and so the unit cancels out during the division.
So you could write something like this:
[<Measure>] type net
[<Measure>] type vatRate = 1
[<Measure>] type vatValue = net * vatRate
let calculateVat (netValue : float<net>) (vat : float<vatRate>) = netValue * vat
let calculateGross (netValue : float<net>) (vat : float<vatValue>) = netValue + vat
This means that float<vatRate> will really be just ordinary float and vatValue is the same as net (but you can still use the aliases in your code as a documentation).
So, this removes the distinction between price with VAT and price without VAT, but at least your program still statically distinguishes between float representing money and float representing just numbers.
As per this question: Fractional power of units of measures in F# there are no fractional powers supported for units of measure in F#.
In my application, it is beneficial to think of data with a metric prefix sometime, e.g. when dealing with seconds. Sometimes I need a result in milli-seconds, sometimes in seconds.
The alternative I'm currently thinking about using is this
[<Measure>] type milli
[<Measure>] type second
let a = 10.0<second>;
let b = 10.0<milli*second>
which gives me:
val a : float<second> = 10.0
val b : float<milli second> = 10.0
Now I want to allow calculations with the two operations. So I could do
let milliSecondsPerSecond = 1000.0<(milli*second)/second>
let a = 10.0<second>;
let b = 10.0<milli*second>
(a*milliSecondsPerSecond) + b
which gives me exactly what I wanted
val it : float<milli second> = 10010.0
Now, this is all nice and shiny but grows out of hand quickly when you want to support multiple units and multiple prefixes. So I think it would be either necessary to bake this into a more generic solution, but don't know where to start. I tried
let milliPer<'a> = 1000.0<(milli * 'a) / 'a>
but that won't work because f# complains and tells me "Non-Zero constants cannot have generic units"...
Since I imagine that unit prefixes are a common problem, I imagine someone has solved this problem before. Is there a more idiomatic way to do unit prefixes in F#?
You write the constant as 1000.0<(milli second)/second> representing 1000 milliseconds per second, but actually (you can do this as an algebraic simplification) "milli" just means that you need to multiply whatever unit by 1000 to get the unit without the "milli" prefix.
So, you can simplify your definition of milliPer (and milliSecondsPerSecond) to just say:
let milli = 1000.0<milli>
Then it is possible to use it with other kinds of measures:
(10.0<second> * milli) + 10.0<milli second>
(10.0<meter> * milli) + 10.0<milli meter>
I think this should not lead to any complications anywhere in the code - it is a perfectly fine pattern when working with units (I've seen people using a unit of percentsimilarly, but then the conversion is 0.01)
Probably a silly question, but I just got started with F# and I've got a little problem.
Say I have a function like this:
let multiplyByTwo x = x * 2
When I call this like this:
let result = multiplyByTwo 5
Everything is alright, the result is 10.
When I call it like this:
let result = multiplyByTwo 2.5
I expect to get 5 or 5.0 as a result. The actual result however is this:
let result = multiplyByTwo 2.5;;
---------------------------------^^^
stdin(4,28): error FS0001: This expression was expected to have type
int
but here has type
float
Because I want this function to be somewhat generic (i.e. accept both floating point numbers and integers), I don't like this. My question of course: how does one solve this?
When you write a numeric literal in F# (such as 2 or 3.14), the compiler treats that as a value of a specific type and so code that uses numeric literals will not be polymorphic. You can either convert input to a single type and work with that type (like float in desco's answer) or use more advanced features of F#...
Certain numeric operations can be written in a polymorphic way, if you mark the code as inline (this way, the compiler can represent additional constraints and statically resolve them) and if you only use polymorphic primitives (with additional static constraints).
Standard operators are polymorpic in inline functions and the F# library provides a way to get polymorphic value representing 1 and 0 (though not 2), but that's enough to write the function you wanted:
let inline twoTimes n =
let one = LanguagePrimitives.GenericOne
n * (one + one)
twoTimes 2
twoTimes 2.0
If you want to make this nicer, you can define a numeric literal (see Daniel's answer to earlier StackOverflow question) and then you can actually write just:
let inline twoTimes n = n * 2G
The special numeric literal 2G is translated to a call to a function of NumericLiteralG which sums specified number of generic 1 values using the technique I used above (so it won't be efficient for large numbers!) For more information, you see also my recent article on writing generic numeric code in F#.
let inline mulBy2 x = (float x) * 2.0
let a = mulBy2 3 // 6.0 : float
let b = mulBy2 2.5 // 5.0 : float
let c = mulBy2 "4" // 8.0 : float
If you aren't afraid using "little hacks", this might be useful:
// Copied from Core.LanguagePrimitives.IntrinsicFunctions.retype
[<NoDynamicInvocation>]
let inline retype (x:'a) : 'b = (# "" x : 'b #)
let inline multiplyByTwo (x:'a) = x * (retype 2:'a)
// use
let result1 = multiplyByTwo 5 // 10
let result2 = multiplyByTwo 2.5 // 5.0
This construct is not type safe since type checking is done in runtime. Also, quotations are relatively slow.
Leaving aside whether we should use units of measure for unitless concepts like angles, suppose I have define degree and radian units in F#
type [<Measure>] degree =
static member ToRadians (d:float<degree>) : float<radian> = d * (Math.PI * 1.<radian>) / 180.0<degree>
and [<Measure>] radian =
static member ToDegrees (r:float<radian>) : float<degree> = r * 180.0<degree> / (Math.PI * 1.<radian>)
I can use them relatively easily like
4.0<degree> |> degree.ToRadians
It seems like extension members would be even handier. So I could just say
let d = 4.0<degree>
let r = d.ToRadians()
But I can't define the extension member the obvious way
type float<degree> with
member degrees.ToRadians() = degree.ToRadians(degrees)
... this gets me the following error
error FS0010: Unexpected identifier in type name. Expected infix operator, quote symbol or other token.
Is there a syntactic trick for extension members on units of measure in F#, or is the feature supported?
F# extension members are different from C# extension members in that you can't define extensions of constructed generic types. For instance, you can define extensions on seq<'t>, but not seq<int>. In other words, extension members really act like members of the type, rather than static methods. This applies to measure types too, so you can't define an extension on float<degree>, but you can define an extension on float<[<Measure>]'u>:
type float<[<Measure>]'u> with
member f.Squared() = f * f
[<Measure>]
type m
let area = 2.0<m>.Squared()
However, I don't see how this helps you in your case...