I like to write some math functions in F# and use them from C#. As F# uses units of measures it is really great to have the compile time check. In C# I already have a Length (indexer that converts to m, mm, km etc) class so I want to use that as a parameter from C# using the F# function and convert it there to float or more specific float.
As I have two parameters I struggle calling the functions. I guess it has something to do with currying.
module Static
open Xunit
open Units.Contract // here is my Length class defined in C#
[<Measure>] type m
[<Measure>] type slabThickness = m
[<Measure>] type kN
[<Measure>] type freshConcreteDensity = kN/m^3
[<Measure>] type freshConcreteLoad = kN/m^2
let FreshConcreteLoad(slabThickness:float<m>, freshConcreteDensity:float<kN/m^3>) = slabThickness * freshConcreteDensity // 1. works
let FreshConcreteLoadL(slabThickness:Length, freshConcreteDensity:Length) = slabThickness.[Length.Units.m] * freshConcreteDensity.[Length.Units.m] //2. works
let FreshConcreteLoadLUseMeasure(slabThickness:Length, freshConcreteDensity:Length) = FreshConcreteLoad (slabThickness.[Length.Units.m]:float<m> freshConcreteDensity.[Length.Units.m]:float<kN/m^3>) //3. here I struggel
// Related to 1. and works
[<Fact>]
let FreshConcreteLoad_Test() =
let thickness = 0.2<slabThickness>
let density = 25.0<freshConcreteDensity>
let load:float<freshConcreteLoad> = FreshConcreteLoad(thickness,density)
Assert.Equal(load, 5.0<kN/m^2>)
// Related to 2. and works
[<Fact>]
let FreshConcreteLoadL_Test() =
let thickness = new Length(0.2)
let density = new Length(25.0) // dont care that this is now also a length, just for testing. in real live here would be a class for kN
let load:float = FreshConcreteLoadL(thickness,density)
Assert.Equal(load, 5.0)
// Related to 3. and I struggle with the function call
[<Fact>]
let FreshConcreteLoadLUseMeasure_Test() =
let thickness = new Length(0.2)
let density = new Length(25.0) // dont care that this is now also a length, just for testing. in real live here would be a class for kN
let load:float = FreshConcreteLoadLUseMeasure(thickness,density)
Assert.Equal(load, 5.0)
Your 3. function definition is not a valid F# function definition. If you need to call another function 'FreshConcreteLoad()' you cannot use type annotations in function call. Maybe you wanted to cast function parameters to units of measure but this cast cannot be performed because F# units of measure are not exist in C# so C# value cannot be directly casted to it.
What you could do is to create a couple of helper converting F# functions like:
let convToLength (inp: float) = inp * 1.0<m>
let convToDensity (inp: float) = inp * 1.0<kN/m^3>
and define 3rd function as:
let FreshConcreteLoadLUseMeasure(slabThickness:Length, freshConcreteDensity:Length) = FreshConcreteLoad (convToLength slabThickness.[Length.Units.m], convToDensity freshConcreteDensity.[Length.Units.m])
or perform conversion directly in the call:
let FreshConcreteLoadLUseMeasure(slabThickness:Length, freshConcreteDensity:Length) = FreshConcreteLoad (1.0<m> * slabThickness.[Length.Units.m], 1.0<kN/m^3> * freshConcreteDensity.[Length.Units.m])