带记录的静态解析类型

发布于 2024-12-12 03:59:07 字数 893 浏览 0 评论 0原文

您可能知道下面的适配器示例:

type Cat() =
member this.Walk() = printf "Cat walking"

type Dog() =
    member this.Walk() = printf "Dog walking"


let inline walk (animal : ^T ) =
    (^T : (member Walk : unit -> unit) (animal))

let cat = new Cat()
let dog = new Dog()

walk cat
walk dog

为 Cat 和 Dog 类静态编译不同版本的 walk。

然后我尝试了以下操作:

type Cat = { Name : string }
type Dog = { Name : string }

let inline showName (animal : ^T ) =
    let name = (^T : (member Name : string) (animal))
    printf "%s" name

let cat = { Name = "Miaou" } : Cat
let dog = { Name = "Waf" } : Dog

showName cat
showName dog

但出现以下编译错误:

The type 'Dog' does not support any operators named 'get_Name'

对于 Cat 类也是如此。

但是,当探索两条记录的生成类时,它实际上包含生成的 Name 属性的 get_Name 方法。

是否有不同的语法来访问静态解析泛型中的记录字段,或者这是 F# 编译器的限制?

You probably know the Adapter example below :

type Cat() =
member this.Walk() = printf "Cat walking"

type Dog() =
    member this.Walk() = printf "Dog walking"


let inline walk (animal : ^T ) =
    (^T : (member Walk : unit -> unit) (animal))

let cat = new Cat()
let dog = new Dog()

walk cat
walk dog

a different version of walk is compiled statically for the Cat and Dog classes.

Then I tried the following :

type Cat = { Name : string }
type Dog = { Name : string }

let inline showName (animal : ^T ) =
    let name = (^T : (member Name : string) (animal))
    printf "%s" name

let cat = { Name = "Miaou" } : Cat
let dog = { Name = "Waf" } : Dog

showName cat
showName dog

but I get the following compilation error :

The type 'Dog' does not support any operators named 'get_Name'

and the same for class Cat.

But when exploring the generated class for both records, it actually contains this get_Name method for the generated Name property.

Is there a different syntax to access Records fields in statically resolved generics, or is it a F# compiler limitation ?

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

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

发布评论

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

评论(2

z祗昰~ 2024-12-19 03:59:07

值得一提的是,记录类型可以实现接口。

type INamedObject =
  abstract Name : string

type Cat = 
  { Name : string }
  interface INamedObject with
    member this.Name = this.Name

type Dog = 
  { Name : string }
  interface INamedObject with
    member this.Name = this.Name

let showName (namedObject : INamedObject) =
    printf "%s" namedObject.Name

你也可以做

type Cat = { Name : string }
type Dog = { Name : string }

let showName (animal : obj) =
  let name =
    match animal with
    | :? Cat as cat -> cat.Name
    | :? Dog as dog -> dog.Name
    | _ -> invalidArg "animal" "Not an animal"
  printf "%s" name

It may be worth mentioning that record types can implement interfaces.

type INamedObject =
  abstract Name : string

type Cat = 
  { Name : string }
  interface INamedObject with
    member this.Name = this.Name

type Dog = 
  { Name : string }
  interface INamedObject with
    member this.Name = this.Name

let showName (namedObject : INamedObject) =
    printf "%s" namedObject.Name

You can also do

type Cat = { Name : string }
type Dog = { Name : string }

let showName (animal : obj) =
  let name =
    match animal with
    | :? Cat as cat -> cat.Name
    | :? Dog as dog -> dog.Name
    | _ -> invalidArg "animal" "Not an animal"
  printf "%s" name
注定孤独终老 2024-12-19 03:59:07

您的 showName 函数实现适用于具有 Name 属性的标准 .NET 类。尽管 CatDog 都不是这样;相反,两者都是F#记录类型。

尽管访问记录字段看起来与访问标准类属性进行 F# 类型推断非常相似,但这两种情况是完全不同的。

您定义了两种具有非唯一字段名称 Name 的记录类型;访问记录类型Cat的实例cat的字段Namecat.Name,与类似狗它是dog.Name。但是,当您尝试 showName catshowNamedog 时,编译器会抱怨这些记录类型中缺少 Name 属性,这是预期的行为,因为有这些记录中没有此类财产。

附录:
为了说明我的观点,我对原始代码做了一点修改,向 CatDog 添加属性 Nickname

type Cat = { Name : string } member x.Nickname = x.Name
type Dog = { Name : string } member x.Nickname = x.Name

let inline showName (animal : ^T ) =
    let name = (^T : (member Nickname : string) (animal))
    printfn "%s" name

let cat = { Name = "Miaou" } : Cat
let dog = { Name = "Waf" } : Dog

showName cat
showName dog

这会很高兴地工作。

注意修改类的签名:现在是

type Cat =
  {Name: string;}
  with
    member Nickname : string
  end

最后,编译器将禁止同时使用类似于 命名的记录类型的字段和属性无法定义成员“Xyzzy”,因为名称“Xyzzy” ' 与此类型或模块消息中的字段“Xyzzy”冲突。

Your showName function implementation applies to standard .NET classes having Name property. Although neither Cat nor Dog are such; instead both are of F# record type.

Despite accessing a record field looks literally similar to accessing a standard class property for F# type inference these two cases are completely different.

You have defined two record types with non-unique field name Name; access to the field Name of instance cat of record type Cat is cat.Name, similarly for dog it is dog.Name. But when you try showName cat or showName dog compiler complains on absense of Name property in these record types, which is expected behavior, because there is no such property in these records.

Addendum:
To illustrate my point I did a slight modification to the original code adding property Nickname to both Cat and Dog:

type Cat = { Name : string } member x.Nickname = x.Name
type Dog = { Name : string } member x.Nickname = x.Name

let inline showName (animal : ^T ) =
    let name = (^T : (member Nickname : string) (animal))
    printfn "%s" name

let cat = { Name = "Miaou" } : Cat
let dog = { Name = "Waf" } : Dog

showName cat
showName dog

This will happily work.

Take a notice of signature of modified classes: it is now

type Cat =
  {Name: string;}
  with
    member Nickname : string
  end

And finally, the compiler will forbid having both a field and a property of a record type similarly named with The member 'Xyzzy' can not be defined because the name 'Xyzzy' clashes with the field 'Xyzzy' in this type or module message.

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