10/04/29 : ペルソナと自我肥大
■[F#][メモ]F#でDuck Typing
F#にはコンパイル時にシグネチャをチェックする仕組みがあるので、
静的型付け言語であるにもかかわらずDuck Typingができちゃう。C++のような感じ。
type Taro() = member self.Speak () = "こんちは!太郎です!" member inline self.Calc (x, y) = x + y type Hanako() = member self.Speak () = "わたしは花子だ。" // Type Extention ※1 type Hanako with member inline self.Calc (x, y) = x - y // 静的に解決される型パラメータ(Statically Resolved Type Parameters) // を使って制約を加えてみる let inline Check (x : ^a when ^a : (member Speak : unit -> string) and ^a : (member Calc : 'm * 'n -> 'o)) = x // Member Constraint Invocation Expressions let inline Speak x = (^a : (member Speak : unit -> string) (Check x)) // 複数の値はタプルにして渡すらしい ※2 let inline Calc x y z = (^a : (member Calc : 'm -> 'n -> 'o) (Check x), y, z) let inline SpeakAndCalc (person : ^a) x y = printfn "%s" <| Speak person Calc person x y let Test () = let taro = SpeakAndCalc <| Taro() let hanako = SpeakAndCalc <| Hanako() taro "hello," "world" |> printfn "%s" hanako 123 23 |> printfn "%d"
> Test () ;; こんちは!太郎です! hello,world わたしは花子だ。 100 val it : unit = ()
Calcメソッドはそれぞれinlineを指定することによって、ここでもさりげなくStatically Resolved Type Parametersの恩恵を受けています。型推論によって総称型と解決されます。
Taro.Calc の型は
member Calc : x: ^a * y: ^b -> ^c
when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
となってます。
(※1)のように、あとから型を拡張したものであっても問題ないみたいです。
(※2)のところでは、型を Calc : 'm -> 'n -> 'o と制限していますが、
このように制限をかけても実際は 'm * 'n -> 'o の関数しか許可してくれません。(なんで?)
ですのでTaro, Hanako のCalcメソッドはタプルを使っています。
こうすることにより、共通のインタフェースがなくとも同じアヒルとして扱うことができます。コンパイル時チェックとはいうものの、シグネチャに違いがあればコードを書いてるそばから知らせてくれます。(VisualStudioすばらしい)
C#だとジェネリックメソッド中で、「'T型の値と'U型の値を足したい・・・!」ということができませんでしたが、F#だとinlineを指定するだけで一発解決! F#かわいいということになります。
でもMSDNにも注意書きがあるように、下手なinline展開はコンパイラの最適化を邪魔する恐れがあるので、利用はやや慎重に。
トラックバック - http://d.hatena.ne.jp/Nobuhisa/20100429/1272475451