F# 和鸭子类型

发布于 2024-11-29 12:21:45 字数 586 浏览 0 评论 0原文

假设我在 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 作为参数,而不是

这不是 F# 在任何情况下都会处理的事情,对吗?看起来 F# 类型推断系统不会比 C# 对 var 类型变量执行更多操作。

Let's say I defined in F# the following two types:

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

I was expecting the following method to work for both cats and dogs:

let isOld x = x.Age >= 65

Actually, what seems to happen is that isOld will only accept cats:

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

let isDogOld = isOld dog //error

My hopes were that F# would be smart enough to define some kind of "virtual" interface X for both cats and dogs so that isOld would accept a X as argument, instead of a Cat.

This isn't something that F# will in any circumstance handle, am I right? It seems like F# type inference system would not do anything more than what the C# does with var typed variables.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

清引 2024-12-06 12:21:45

您可以定义带有成员约束的内联函数,或者采用经典路线并使用接口(在这种情况下可能是首选)。

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

You can define an inline function with a member constraint, or go the classic route and use an interface (which would probably be preferred in this case).

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

EDIT

I just remembered this won't work for record types. Technically their members are fields, although you can amend them with members using with member .... You would have to do that to satisfy an interface anyway.

For reference, here's how you would implement an interface with a record type:

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
々眼睛长脚气 2024-12-06 12:21:45

通常,F# 鸭子类型意味着编译时多态性。语法有点奇怪,但您应该能够从以下示例中算出它 -

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" }

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

Usually what is meant by F# duck-typing is a compile-time polymorphism. Syntax is a little weirder, but you should be able to work it out from the following example -

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" }

Remember, this polymorphism only works at compile-time!

梦里泪两行 2024-12-06 12:21:45

FSharp.Interop.Dynamic(在 nuget 上)提供了基于 DLR 的实现动态运算符(真正的动态鸭子类型)

let isOld x = x?Age >= 65

FSharp.Interop.Dynamic (on nuget) provides a DLR based implementation of the dynamic operator (real dynamic duck typing)

let isOld x = x?Age >= 65
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文