Lee Crabtree
Lee Crabtree

Reputation: 1256

F# overly aggressive type inference?

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?

Upvotes: 4

Views: 292

Answers (2)

Gus
Gus

Reputation: 26204

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.

Upvotes: 6

kvb
kvb

Reputation: 55185

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.

Upvotes: 3

Related Questions