8
votes

Is it possible to somehow create a pow function for measure types? The pow function in f# only takes int as parameter, and then pow function in the Math class takes a float - but dosent allow float<cm>.

I first thought that:

let rec myPow(x:float<cm>,y:int) =
    if y = 0 then x
    else myPow(x*x, y - 1)

might work out, but its obvious that each time it come across the else line it will change the return type.

Any suggestions?

3
Note that your code calculates x^(2^y), not x^y.hammar

3 Answers

7
votes

I don't think that is possible. You are asking the function to return <cm^2> in case the power is by 2 and <cm^3> in case of 3 and so on. Which makes the function to return different "types" based on the calculation which obviously not possible in a static type and type safe language. Unfortunately, I don't think units of measure can be made "generics" to try that to reach any further.

Your function can have only one static return type.

6
votes

Ankur is correct - you cannot do this (without resorting to hacks that would break units).

Maybe a clearer description of the problem is that the type of pow function would depend on the value of the argument and F# doesn't allow you to do this. You could imagine this would work if were using just literals as the second argument, but it would become tricky if you used expressions:

pow a 3 // Assuming a = 1.0<cm>, the return type is float<cm ^ 3>
pow a n // Assuming a = 1.0<cm>, the return type is float<cm ^ n>

In the second case the value n would have to appear in the type!

You can use some nasty tricks (inspired by this Haskell article), but it becomes a bit crazy. Instead of using numeric literals, you'd use something like S(S(S(N))) to represent the number 3. This way, you can bring the number into the type. You probably don't want to do this, but here is an example:

[<Measure>] type cm

// Represents a number with units of measure powered to the
// number's value (e.g "(S (S O))" has type Num<cm, cm^3>)
type Num<[<Measure>] 'M, [<Measure>] 'N> = 
  | O_ of int * float<'N>
  | S_ of int * Num<'M, 'N / 'M>

// Constructors that hide that simplify the creation  
let O : Num<'M, 'M> = O_ (1, 0.0<_>)
let S n = match n with O_(i, _) | S_(i, _) -> S_(i + 1, n)

// Type-safe power function with units of measure
let pow (x:float<'M>) ((O_(i, _) | S_(i, _)):Num<'M, 'M 'N>) : float<'M 'N> =
  // Unsafe hacky implementation, which is hidden
  // from the user (for simplicity)
  unbox ((float x) ** float i)

let res = pow 2.0<cm> (S (S O))

EDIT: I posted the source code to F# snippets, so that you can see the inferred types: http://fssnip.net/4H

4
votes

As said, you cannot. If y is not known at compile-time, it's not possible to type check the expression in F# type system.

I suspect you'll use myPow only with a few small and known constants. In this case, you could use the following functions instead and keep static typing:

let inline pow2 (x: float<'a>) : float<'a^2> = pown (float x) 2 * 1.<_>
let inline pow3 (x: float<'a>) : float<'a^3> = pown (float x) 3 * 1.<_>
let inline pow4 (x: float<'a>) : float<'a^4> = pown (float x) 4 * 1.<_>
let inline pow5 (x: float<'a>) : float<'a^5> = pown (float x) 5 * 1.<_>