ダックタイピング

6.4.8 Member Constraint Invocation Expressionsという機能があって、ダックタイピングできるようです。F#でDuck Typingも参考になりました。

マニュアルに書いてあるのを少しだけ変更。パラメータをもつメソッドも呼び出してみました。

type Duck() =
member x.Speak(greeting) = sprintf "%s. I'm a duck." greeting
member x.MakeNoise() = "quack."

type Dog() =
member x.Speak(greeting) = greeting + ". I'm a dog."
member x.MakeNoise() = "grrrr."

[<Test>]
let ``test Member Constraint Invocation Expressions`` () =
let inline speak (a: ^a) greeting =
let x = (^a : (member Speak: string -> string) (a, greeting))
let y = (^a : (member MakeNoise: unit -> string) (a))
sprintf "%s %s" x y

let duck = Duck()
let dog = Dog()
Assert.AreEqual("Hello. I'm a duck. quack.", speak duck "Hello")
Assert.AreEqual("Hi. I'm a dog. grrrr.", speak dog "Hi")


おもしろいんですが、これといった使い道が思い浮かばない。ふだんダックタイピングとかしないので。普通はインタフェースを使うんでしょうね。インタフェース & オブジェクト式を使った版もつくってみました。コード量はちょっと多くなりますね。

type Animal =
abstract Speak : string -> string
abstract MakeNoise : unit -> string

[<Test>]
let ``test Object Expressions`` () =
let speak (a : Animal) greeting =
let x = a.Speak(greeting)
let y = a.MakeNoise()
sprintf "%s %s" x y

let duck = Duck()
let duck = { new Animal with
member
x.Speak(greeting) = duck.Speak(greeting)
member x.MakeNoise() = duck.MakeNoise() }
let dog = Dog()
let dog = { new Animal with
member
x.Speak(greeting) = dog.Speak(greeting)
member x.MakeNoise() = dog.MakeNoise() }
Assert.AreEqual("Hello. I'm a duck. quack.", speak duck "Hello")
Assert.AreEqual("Hi. I'm a dog. grrrr.", speak dog "Hi")