F # и утка - PullRequest
       59

F # и утка

14 голосов
/ 15 августа 2011

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

Ответы [ 3 ]

15 голосов
/ 15 августа 2011

Вы можете определить функцию inline с ограничением члена, или пойти по классическому маршруту и ​​использовать интерфейс (который, вероятно, предпочтительнее в этом случае).

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

EDIT

Я только что вспомнил, что это не будет работать для типов записей. Технически их участники являются полями, хотя вы можете изменить их с помощью 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
8 голосов
/ 10 февраля 2014

Обычно 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" }

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

4 голосов
/ 15 августа 2011

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

let isOld x = x?Age >= 65
...