views:

249

answers:

2

(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
A: 

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: http://stackoverflow.com/questions/419521/f-units-of-measure-lifting-values-to-floatsomething

Robert
I asked that one too, I must be a bit slow on the uptake! I guess this means I have two options - either I force to a given unit, or I pass in my zero value?
Benjol
I'm not sure I understand what you mean by cannot be used as type parameters, 'cos this works: let fn (a:float<'a>) (b:float<'a>) = a * b
Benjol
+1  A: 

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, ...

Brian
I'll let this stand as the answer, as it answered two thirds of my question. And thanks for the reminder about naming convention. I'd have liked to avoid the upcast too.
Benjol