F# Units of measure, problems with genericity
-
19-08-2019 - |
Question
(I'm still banging on with units of measure in F#)
I'm having a problem making 'generic' functions which take 'typed' floats.
The following mockup class is intended to keep tabs on a cumulative error in position, based on a factor 'c'. The compiler doesn't like me saying 0.<'a> in the body of the type ("Unexpected type parameter in unit-of-measure literal").
///Corrects cumulative error in position based on s and c
type Corrector(s_init:float<'a>) =
let deltaS ds c = sin (ds / c) //incremental error function
//mutable values
let mutable nominal_s = s_init
let mutable error_s = 0.<'a> //<-- COMPILER NO LIKE
///Set new start pos and reset error to zero
member sc.Reset(s) =
nominal_s <- s
error_s <- 0.<'a> //<-- COMPILER NO LIKE
///Pass in new pos and c to corrector, returns corrected s and current error
member sc.Next(s:float<'a>, c:float<'a>) =
let ds = s - nominal_s //distance since last request
nominal_s <- s //update nominal s
error_s <- error_s + (deltaS ds c) //calculate cumulative error
(nominal_s + error_s, error_s) //pass back tuple
Another related question, I believe, still to do with 'generic' functions.
In the following code, what I am trying to do is make a function which will take a #seq of any type of floats and apply it to a function which only accepts 'vanilla' floats. The third line gives a 'Value Restriction' error, and I can't see any way out. (Removing the # solves the problem, but I'd like to avoid having to write the same thing for lists, seqs, arrays etc.)
[<Measure>] type km //define a unit of measure
let someFloatFn x = x + 1.2 //this is a function which takes 'vanilla' floats
let MapSeqToNonUnitFunction (x:#seq<float<'a>>) = Seq.map (float >> someFloatFn) x
let testList = [ 1 .. 4 ] |> List.map float |> List.map ((*) 1.0<km>)
MapSeqToNonUnitFunction testList
Solution
You can change the first 'compiler no like' to
let mutable error_s : float<'a> = 0.0<_>
and the compiler seems to like that.
As for the second question, I am not seeing the same error as you, and this
[<Measure>] type km
//define a unit of measure
let someFloatFn x = x + 1.2 //this is a function which takes 'vanilla' floats
let MapSeqToNonUnitFunction (x:seq<float<_>>) = Seq.map (float >> someFloatFn) x
let testList = [ 1 .. 4 ] |> List.map float |> List.map ((*) 1.0<km>)
let testList2 = testList :> seq<_>
let result = MapSeqToNonUnitFunction testList2
printfn "%A" result
compiles for me (though the upcast to seq<_> is a little annoying, I am not sure if there is an easy way to get rid of it or not).
Aside, I think convention is to name units parameters 'u, 'v, ... rather than 'a, 'b, ...
OTHER TIPS
Units of measure cannot be used as type parameters. This is because the are erased by the compiler during compilation. This question is quite similar: F# Units of measure - 'lifting' values to float<something>