Статически разрешенные типы с записями - PullRequest
3 голосов
/ 26 октября 2011

Вы, вероятно, знаете пример адаптера ниже:

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.

Тогда я попробовал следующее:

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.

Но при исследовании сгенерированного класса для обеих записей он фактически содержит метод get_Name для сгенерированного свойства Name.

Существует ли другой синтаксис для доступа к полям записей в статически разрешенных шаблонах или это ограничение компилятора F #?

Ответы [ 2 ]

4 голосов
/ 26 октября 2011

Стоит отметить, что типы записей могут реализовывать интерфейсы.

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
3 голосов
/ 26 октября 2011

Ваша реализация функции showName применяется к стандартным классам .NET, имеющим свойство Name. Хотя ни Cat, ни Dog не являются таковыми; вместо этого оба типа F # record .

Несмотря на доступ к записи field выглядит буквально для доступа к стандартному классу property для вывода типа F #, эти два случая совершенно разные.

Вы определили два типа записей с неуникальным именем поля Name; доступ к полю Name экземпляра cat типа записи Cat равен cat.Name, аналогично для dog это dog.Name. Но при попытке showName cat или showName dog компилятор жалуется на отсутствие свойства Name в этих типах записей, что является ожидаемым поведением, поскольку в этих записях такого свойства нет.

Добавление: Чтобы проиллюстрировать свою точку зрения, я сделал небольшое изменение в исходном коде, добавив свойство Nickname к Cat и 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

Это с радостью сработает.

Обратите внимание на сигнатуру модифицированных классов: теперь

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

И, наконец, компилятор запретит иметь как поле, так и свойство типа записи с аналогичным именем в сообщении The member 'Xyzzy' can not be defined because the name 'Xyzzy' clashes with the field 'Xyzzy' in this type or module.

...