かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

手軽なスクリプト言語としてのF# その16「総称型 ジェネリック」

F#にもジェネリックあります!今までもさらりと使ってたり使ってなかったりしますが、これはF#のコンパイラが最大限に空気を読んで、ジェネリックに出来そうなところは勝手にしてくれるからです。例えば以下のような関数を書いたとします。

let makeTuple x y = (x, y)

特に実用性のかけらもない関数ですが引数で渡された2つの値をタプルにして返します。こいつの定義は実は以下のようになってたりします。

'a -> 'b -> 'a * 'b

F#では型引数は、'aのようにシングルクォーテーションを使って表します。上記の定義では何かの型aを受け取って、何かの型bを受け取って'a * 'bのタプルを返すというものになります。このように、適当に関数を定義しておくと勝手にジェネリック型にしてくれます。試しにmakeTupleを使ってみます。

let makeTuple x y = (x, y)

printfn "%A" <| makeTuple 1 "one"
printfn "%A" <| makeTuple 2.1 1

makeTupleにintを渡したりfloatを渡したりstringを渡したりしています。実行結果は以下のようになります。

(1, "one")
(2.1, 1)

ちなみに明示的にジェネリックだよということを関数の定義時に示すことも出来ます。makeTupleの場合以下のように定義出来ます。

let makeTuple (x : 'a) (y : 'b) = (x, y)
let makeTuple<'T, 'U> (x : 'T) (y : 'U) = (x, y)

何パターンか書き方があるので、自分が馴染む書き方でいいと思いますが、コンパイラがかなり面倒みてるので自分で型引数を明示するということはあまりないと思います。強いて言えば型引数に制約をつけるときくらいかな。


例えば、型引数があるインターフェースを実装してないとダメという制約をつける場合には以下のように記述します。

// Showを持ってるというだけのインターフェース
type IShowable =
    abstract Show : unit -> unit

// TはIShowableじゃないとダメ
let callShow<'T when 'T :> IShowable> (x : 'T)  =
    // 制約でIShowableであると明示してるのでShowが呼べる
    x.Show()

// さらっと新構文
// その場で型を定義してインスタンス生成。匿名型かな
let s1 = { 
    new obj()
        interface IShowable with
            override this.Show() =
                printfn "Showが呼ばれました"
    }

// s1はIShowableを実装しているのでcallShowに渡せる
callShow s1

「型引数 when 制約をつけたい型引数 :> 型名」のように制約を記述します。他にも色々制約をつけれたりするのですが、一番使うのはこの制約だと思います。他のは、MSDNで確認するといいと思うよ!