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
.
Ответы
Ответ 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