17

Let's say I defined in F# the following two types:

type Dog = { DogName:string; Age:int }
type Cat = { CatName:string; Age:int }

I was expecting the following method to work for both cats and dogs:

let isOld x = x.Age >= 65

Actually, what seems to happen is that isOld will only accept cats:

let dog = { DogName = "Jackie"; Age = 4 }
let cat = { CatName = "Micky"; Age = 80 }

let isDogOld = isOld dog //error

My hopes were that F# would be smart enough to define some kind of "virtual" interface X for both cats and dogs so that isOld would accept a X as argument, instead of a Cat.

This isn't something that F# will in any circumstance handle, am I right? It seems like F# type inference system would not do anything more than what the C# does with var typed variables.

4

3 に答える 3

19

You can define an inline function with a member constraint, or go the classic route and use an interface (which would probably be preferred in this case).

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

EDIT

I just remembered this won't work for record types. Technically their members are fields, although you can amend them with members using with member .... You would have to do that to satisfy an interface anyway.

For reference, here's how you would implement an interface with a record type:

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
于 2011-08-15T14:21:44.657 に答える
8

Usually what is meant by F# duck-typing is a compile-time polymorphism. Syntax is a little weirder, but you should be able to work it out from the following example -

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" }

Remember, this polymorphism only works at compile-time!

于 2014-02-10T05:54:26.400 に答える
4

FSharp.Interop.Dynamic (on nuget) provides a DLR based implementation of the dynamic operator (real dynamic duck typing)

let isOld x = x?Age >= 65
于 2011-08-15T14:41:02.197 に答える