7

I'd like to use statically resolved type parameters with some extension methods I added to built-in types, like float32 and int32, so I tried the following:

module Foo =
    type System.Single with
        static member Bar x = x + 1.0f

    let inline bar (x : ^a) =
        (^a : (static member Bar : ^a -> ^a) x)

open Foo
[<EntryPoint>]
let main argv =
    System.Console.WriteLine (bar 1.0f) (* Compilation fails here *)
    0

The compiler complains that The type 'float32' doesn't support the operator 'Bar'. What am I doing wrong?

1 Answer 1

9

Statically resolved type parameters will not resolve against extension methods so this particular approach isn't possible.

I would advise thinking carefully about whether this is something you really need to do or whether you can express the problem you're trying to solve in a different way.

If you are sure that this is what you want and you're willing to brave the dragons that follow, there is a workaround which involves creating a helper type with some static methods:

type Ext = Ext
    with
        static member Bar (ext : Ext, flt : float) = 1.0 + flt
        static member Bar (ext : Ext, flt : float32) = 1.0f + flt

You can then define a new function using statically resolved type parameters like this:

let inline bar (x : ^a) =
    ((^b or ^a) : (static member Bar : ^b * ^a -> ^a) (Ext, x))

Then you can write:

bar 7.0f
val it : float32 = 8.0f

bar 5.0
val it : float = 6.0
Sign up to request clarification or add additional context in comments.

7 Comments

Seems like quite a nice and clean solution. Where are the dragons hiding?
Also, how does the core implementation do things like sin, cos and the likes? From the docs, they operate on types which have a static member Sin/Cos, but I know for a fact that standard CLR doesn't have a static System.Single.Sin
Turns out they are employing a special undocumented usage of the when keyword...: stackoverflow.com/questions/26406664/…
@FrancescoBertolaccini That particular use of when syntax is only allowed within the F# core library, you will get a compiler error if you try to employ it outside of that context.
@FrancescoBertolaccini I can see it being problematic because extension methods aren't intrinsically part of the type, which extension methods are available is determined by which namespaces are open, etc. The best general solution, in my opinion, would be a full implementation of type classes/traits. There is some discussion of this here: github.com/fsharp/fslang-suggestions/issues/243
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.