4

So in doing some of the Project Euler problems, I want to be able to take the square root of integer values (int, long, bigint, etc), but Sqrt is only defined for floating-point values. So I've been writing my own little Newton-Raphson algorithm, and it's plenty accurate for what I need. However, I want to be able to call the built-in sqrt function on floating-point values. So I wrote something like this:

let inline dsqrt x =
    match box x with
    | :? float -> sqrt x
    | :? float32 -> sqrt x
    | _ -> p_dsqrt x

My function, obviously, is named "p_dsqrt". However, this function requires that the input have a Sqrt method defined, which sort of defeats the whole purpose. Am I missing some type constraint, or what?

Lee Crabtree
  • 1,196
  • 2
  • 12
  • 30

2 Answers2

6

If you want to use the match, the inline keyword is not required but if you want to use an inline function and "hat types", use overloading instead of match:

type Sqrt = Sqrt with
    // Dummy overload in order to get the right types inferred (will never reach here)
    static member inline ($) (Sqrt, _:^t when ^t:null and ^t: struct) = id

    // Existing sqrt
    static member inline ($) (Sqrt, x:'a) :'a = sqrt x 

    // Your Newton-Raphson based sqrt's
    static member        ($) (Sqrt, x:int   ) = sqrtForInt    x
    static member        ($) (Sqrt, x:bigint) = sqrtForBigInt x 

let inline sqrt (x:'t) :'t = Sqrt $ x 

The return type will always be the same as the input type and the implementation of sqrt chosen will depend on that type. This selection will happen at compile-time which is the main difference with the match method which is resolved at run-time.

If I take out the dummy overload, it will have the same problem as your code: it will require the sqrt constraint.

Gus
  • 25,839
  • 2
  • 51
  • 76
3

I think you probably want this, instead:

let dsqrt x =
    match box x with
    | :? float as f -> sqrt f |> box :?> 'a
    | :? float32 as f -> sqrt f |> box :?> 'a
    | _ -> p_dsqrt x

The problem with your code is that you're directly calling sqrt x, which constrains the possible types of x. In my modified code I bind a new identifier to the result of the successful coercion to float or float32, so this doesn't put any constraint on the type of x.

kvb
  • 54,864
  • 2
  • 91
  • 133
  • 1
    Shouldn't `dsqrt` still be `inline`, though, to correctly propagate `x`'s type to `p_dsqrt`? – ildjarn Sep 24 '12 at 20:59
  • Unfortunately, I'm just getting a different error now. I get "The type 'float' does not match the type 'float32'". When I remove the 'float32' case, the function still resolves to "float -> float". For reference, my p_dsqrt function resolves to "'a -> 'a". – Lee Crabtree Sep 24 '12 at 21:07
  • @LeeCrabtree - sorry, I updated my answer. There needs to be a dynamic conversion back to the generic type, too. – kvb Sep 24 '12 at 21:11