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 Answers1

8

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
Community
  • 1
  • 1
TheInnerLight
  • 12,034
  • 1
  • 29
  • 52
  • Seems like quite a nice and clean solution. Where are the dragons hiding? – Francesco Bertolaccini Aug 06 '17 at 06:44
  • 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` – Francesco Bertolaccini Aug 06 '17 at 07:32
  • Turns out they are employing a special undocumented usage of the `when` keyword...: https://stackoverflow.com/questions/26406664/undocumented-when-keyword-usage-in-fsharp-core – Francesco Bertolaccini Aug 06 '17 at 07:52
  • @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. – TheInnerLight Aug 06 '17 at 09:24
  • I know, quite a bummer. Feels like cheating on their side :/ I guess yours is the best way for now. Is there a technical reason why my way wouldn't be possible to implement in the compiler? (I.e. is it worth opening an issue for that in the fsharp repo?) – Francesco Bertolaccini Aug 06 '17 at 09:27
  • 1
    @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: https://github.com/fsharp/fslang-suggestions/issues/243 – TheInnerLight Aug 06 '17 at 10:36
  • It would still be checkable at compile time, though, right? We already need to check which namespaces are open for standard extension methods, so it shouldn't be _much_ problematic... – Francesco Bertolaccini Aug 06 '17 at 10:41