F# - 函数重载

21

有没有一种方法可以重载一个函数?

我们来看这三个函数:

// Returns StringPropertyInfo
let stringProperty (expr:Expr<'a -> string>) (cfg:EntityInfo<'a>) = 
    cfg.Property expr

// Returns DatePropertyInfo
let dateProperty (expr:Expr<'a -> System.DateTime>) (cfg:EntityInfo<'a>) = 
    cfg.Property expr

// Returns BytePropertyInfo
let byteProperty (expr:Expr<'a -> System.Byte>) (cfg:EntityInfo<'a>) =
     cfg.Property expr
有没有一种方法可以将它们全部合并为一个?
let property expr cfg = ....

如果不行的话,有没有更简洁的方法来实现类似的效果?

4个回答

19

如果您想使用基于区分联合的方法,那么我认为声明更适合(因为您不需要处理引用)。根据Alex建议的类型进行轻微修改:

type PropertyInfo<'a> =
  | String of Expr<'a -> string>
  | Date of Expr<'a -> System.DateTime>
  | ...

那么你可以这样写:

let property (pi:PropertyInfo<'a>) (cfg:EntityInfo<'a>) =
  match pi with
  | String e -> cfg.Property e
  | ...

cfg |> property (String <@ fun e -> e.Foo @>)

另一种选择是将property实现为类型的静态成员,这样您就可以使用常规重载(类似于C#)。类似于下面的内容:

type EF = 
  static member property (expr:Expr<'a -> string>) (cfg:EntityInfo<'a>) = 
    cfg.Property expr
  static member property (expr:Expr<'a -> System.DateTime>) (cfg:EntityInfo<'a>) = 
    cfg.Property expr
  static member property (expr:Expr<'a -> System.Byte>) (cfg:EntityInfo<'a>) =
     cfg.Property expr

然后你会写:

cfg |> EF.property <@ e -> e.Foo @>

最后,你也可以使函数变得更简单(但不太安全),通过使函数完全通用并进行动态类型测试(以决定使用什么返回类型)。类似于:

let property<'a, 'r> (e:Expr<'a -> 'r>) (cfg:EntityInfo<'a>) =
  if typeof<'r> = typeof<string> then
    // ...

没错! - 但是似乎你不能在一个判别联合中定义泛型?至少 | String of Expr<'a -> string> 会抛出编译器错误,指出 类型参数'a'未定义 - ebb
@ebb:那是我的错误——更新后的答案应该可以工作(类型PropertyInfo需要本身是泛型,以便它可以在内部具有类型变量'a)。 - Tomas Petricek
1
我明白了,但是看起来鉴别联合并不是可行的选择,因为我想要重载的函数将返回不同的对象。 - ebb

12

简短回答,不行。这里有另一个处理此问题的问题

但是你可以在类中重载方法,否则.NET集成效果就不太好。这是一种可能的解决方案,但在我看来并不是很好。

你可以将它们放在不同的子模块中。

也许你可以编写通用函数并通过其中一个参数决定返回类型。然而,当涉及类型推断时,我对此还是个新手,不能确定。


7
我认为在这种情况下,一种带标签联合类型会对你有所帮助。你可以像这样编写代码:
type PropertyInfo = 
| StringPropertyInfo of string
| DatePropertyInfo of System.DateTime
| BytePropertyInfo of byte

然后会匹配它并执行适当的操作,在一个函数中返回联合结果...

我认为这不会很容易使用。使用这个判别联合,你必须使用 Expr<'a -> PropertyInfo>,但是 Property 方法需要一个不同的引用 - 所以 OP 必须对引用进行一些预处理才能使其正常工作。然而,将整个引用包装在判别联合中应该很容易使用。 - Tomas Petricek

4

要得到您想要的结果,一种比较麻烦的方法是稍微泛化一下并使用以下代码:

let inline property expr cfg =
    (^t : (member Property : Expr<'a -> 'b> -> 'c)(cfg, expr))

然而,只要它们具有正确签名的Property成员,这也可以应用于类型为EntityInfo<'a>以外的其他类型的值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接