F#和鸭子打字

dev*_*ium 15 .net f# functional-programming type-inference duck-typing

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

type Dog = { DogName:string; Age:int }
type Cat = { CatName:string; Age:int }
Run Code Online (Sandbox Code Playgroud)

我期待以下方法适用于猫和狗:

let isOld x = x.Age >= 65
Run Code Online (Sandbox Code Playgroud)

实际上,似乎isOld只会接受猫:

let dog = { DogName = "Jackie"; Age = 4 }
let cat = { CatName = "Micky"; Age = 80 }

let isDogOld = isOld dog //error
Run Code Online (Sandbox Code Playgroud)

我希望F#足够聪明,可以X为猫和狗定义某种"虚拟"界面,这样就isOld可以接受X作为参数,而不是a Cat.

这不是F#在任何情况下处理的事情,我是对的吗?似乎F#类型的推理系统不会做任何比C#对var类型变量做的更多的事情.

Dan*_*iel 16

您可以inline使用成员约束定义函数,或者使用经典路径并使用接口(在这种情况下可能是首选).

let inline isOld (x:^T) = (^T : (member Age : int) x) >= 65
Run Code Online (Sandbox Code Playgroud)

编辑

我记得这对记录类型不起作用.从技术上讲,他们的成员都是字段,但您可以使用成员修改它们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
Run Code Online (Sandbox Code Playgroud)


Bry*_*dds 8

通常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" }
Run Code Online (Sandbox Code Playgroud)

请记住,这种多态性仅适用于编译时!