2015-10-02 122 views
7

考虑我有两个不同的类型:联函数和类型的扩展名

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

我想实现通用功能zoo将为要么FooBar情况下工作。 而且我无法更改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类型定义的情况下解决问题?

回答

11

使用方法过载。

扩展方法的问题在于,当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#调用它,如果需要的话不要使用函数,请使用简单和标准的方法重载。

1

我能得到迄今为止最好的事情是

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" 

这是不是最好的,而不是非常通用的解决方案,但至少它的工作原理。