F# and duck-typing



假设我在f#中定义了以下两种类型:

type Dog = { DogName:string; Age:int }
type Cat = { CatName:string; Age:int }

我希望下面的方法对猫和狗都有效:

let isOld x = x.Age >= 65

实际上,isOld只接受猫:

let dog = { DogName = "Jackie"; Age = 4 }
let cat = { CatName = "Micky"; Age = 80 }
let isDogOld = isOld dog //error

我希望f#足够聪明,可以为猫和狗定义某种"虚拟"接口X,以便isOld接受X作为参数,而不是Cat

这不是f#在任何情况下都会处理的东西,对吗?似乎f#类型推断系统不会做任何比c#对var类型变量所做的更多的事情。

您可以使用成员约束定义inline函数,或者采用经典的方法并使用接口(在这种情况下可能更可取)。

let inline isOld (x:^T) = (^T : (member Age : int) x) >= 65

编辑

我刚想起来这对记录类型不起作用。从技术上讲,它们的成员是字段,尽管您可以使用with member ...使用成员修改它们。无论如何,你必须这样做才能满足接口。

作为参考,下面是如何实现记录类型的接口:

type IAging =
  abstract Age : int
type Dog = 
  { DogName : string
    Age : int } 
  interface IAging with
    member this.Age = //could also be `this.Age = this.Age`
      let { DogName = _; Age = age } = this
      age

通常f# duck-typing指的是编译时多态性。语法有点奇怪,但您应该能够从下面的示例中计算出来-

module DuckTyping
// Demonstrates F#'s compile-time duck-typing.
type RedDuck =
    { Name : string }
    member this.Quack () = "Red"
type BlueDuck =
    { Name : string }
    member this.Quack () = "Blue"
let inline name this =
    (^a : (member Name : string) this)
let inline quack this =
    (^a : (member Quack : unit -> string) this)
let howard = name { RedDuck.Name = "Howard" }
let bob = name { BlueDuck.Name = "Bob" }
let red = quack { RedDuck.Name = "Jim" }
let blue = quack { BlueDuck.Name = "Fred" }

记住,这种多态性只在编译时有效!

FSharp.Interop。Dynamic(基于nuget)提供基于DLR的动态操作符实现(真正的动态duck类型)

let isOld x = x?Age >= 65

最新更新