内联函数和类型扩展

Inline function and type extension

假设我有两个不同的 library 类型:

type Foo = { foo : string }
type Bar = { bar : int32 }

我想实现适用于 FooBar 实例的通用函数 zoo而且我无法更改 FooBar,因为它们是库代码的一部分。

这是我第一次尝试使用类型扩展和内联函数 here:

// Library.fs
module Library

type Foo = { foo : string }
type Bar = { bar : int32 }

// Program.fs
type Foo with
    static member zoo (f : Foo) = "foo"

type Bar with
    static member zoo (b : Bar) = "bar"

let inline zoo (x : ^t) =
    (^t : (static member zoo : ^t -> string) x)

let f = zoo { foo = "1" } // error FS0001: The type 'Foo' does not support the operator 'zoo'

为什么内联函数定义不依赖于类型扩展?如何在不更改初始 FooBar 类型定义的情况下解决我的问题?

到目前为止我能得到的最好的东西是

type Ext =
    static member zoo (f : Foo) = "foo"
    static member zoo (b : Bar) = "bar"

let f = Ext.zoo { foo = "1" } // "foo"
let b = Ext.zoo { bar = 2 } // "bar"

这不是最好的解决方案,也不是很通用,但至少它有效。

使用方法重载。

扩展方法的问题是在 solving member constraints 时没有考虑到扩展方法。

因此您可以使用方法重载,如您自己的答案中所示,或者您可以更进一步,通过使用中间类型和中间方法(在本例中为简单起见,使用运算符)来创建内联泛型函数诀窍:

type T = T with
    static member ($) (T, x:Foo) = "foo"
    static member ($) (T, x:Bar) = "bar"

let inline zoo x = T $ x

let f = zoo { foo = "1" }

这里有 more details 其工作原理。

请注意,此函数将被内联,因此例如您将无法从 C# 调用它,如果需要,请不要使用函数,使用简单和标准的方法重载。