Подтвердить что ты не робот

F # и утиная печать

Скажем, я определил в 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 # не будет делать ничего, кроме того, что делает С# с переменными var.

4b9b3361

Ответ 1

Вы можете определить функцию 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

Ответ 2

Обычно, что подразумевается под F # duck-typing, это полиморфизм времени компиляции. Синтаксис немного страннее, но вы должны уметь его выработать из следующего примера -

module DuckTyping

// Demonstrates F# 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" }

Помните, что этот полиморфизм работает только во время компиляции!

Ответ 3

FSharp.Interop.Dynamic (на nuget) обеспечивает DLR-реализацию динамического оператора (реальная динамическая утиная печать)

let isOld x = x?Age >= 65