書いて覚えるSwift入門

第2回 総称関数!=関数?

この記事を読むのに必要な時間:およそ 3 分

総称関数の基本

連載第2回目の今回は,前回予告どおりSwiftにおける総称関数(Generic Functions)を詳しくみていきます。が,まず基本をおさらいしてみましょう。基本なので最も単純なケースから。最も単純な関数というと,引数をそのまま返す関数でしょう。

JavaScriptなら,

function identity(x) {
  return x;
}

Perlなら,

sub identity {
  shift;
}

Pythonなら

def identity(x):
  return x

Ruby なら,

def identity(x):
  x
end

どれも実質1行。簡単ですね。

ところが動的言語にとってこれほど簡単なことが,静的言語にとっては難しい。これをCでやってみようとすると……。

int int_identity(int x){
  return x;
}
double double_identity(double x){
  return x;
}
char *str_identity_s(char *x){
  return x;
}

要するに型の数だけ実装が必要になってしまうのです。中身が同じでも,型が違えば別物である以上,静的言語ではこれが当然でした。総称関数が登場するまでは。その総称関数で書くとどうなるのでしょうか?

こうなります。

func identity<T>(x:T)->T {
  return x
}

実際に動かして確認してみましょう図1)。

図1 総称関数を動かしてみる

図1 総称関数を動かしてみる

let i = identity(42)
let d = identity(42.195)
let s = identity("Marathon")

確かに動いています。

ここで<T>を取っ払ってみましょう。どうなりましたか? Use of undeclared type Tというエラーが出たはずです。これで<T>の役割がわかりました。Tは型の名前ではなく型の変数なので,コンパイル時に適切な型に置き換えた関数を作ってね」とSwiftにお願いしているわけです。

ここで問題です。上記のidentity(42)identityと,identity(42.195)identityは同じものでしょうか?

コンパイルされたコードを見てみればリスト1),明らかです。

リスト1 identityの自分探し

% nm identity
0000000100000ee0 T __TF8identity8identityU__FQ_Q_
                 U __TFSSCfMSSFT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS
                 U __TFSsa6C_ARGCVSs5Int32
                 U __TFSsa6C_ARGVGVSs20UnsafeMutablePointerGS_VSs4Int8__
                 U __TMdSS
                 U __TMdSd
                 U __TMdSi
0000000100001050 S __Tv8identity1dSd
0000000100001048 S __Tv8identity1iSi
0000000100001058 S __Tv8identity1sSS
0000000100000000 T __mh_execute_header
0000000100000f10 T _main
0000000100000e10 t _top_level_code
                 U dyld_stub_binder

_Tv8identity1までが同じで,後が異なるシンボルが3つ出てきました。プログラマが書いた1つのコードから,それぞれの型に対応する関数が3つ生成されたのです。

さらに,実際にidentityを使用している行をコメントアウトしてコンパイルしなおすと,シンボルはこうなりますリスト2)。

リスト2 identityのシンボル探し

% nm identity
0000000100000f00 T __TF8identity8identityU__FQ_Q_
                 U __TFSsa6C_ARGCVSs5Int32
                 U __TFSsa6C_ARGVGVSs20UnsafeMutablePointerGS_VSs4Int8__
0000000100000000 T __mh_execute_header
0000000100000f30 T _main
0000000100000ef0 t _top_level_code
                 U dyld_stub_binder

identityは完全に消えてしまいました。このことから,驚くべき結論が得られます。

総称関数は,関数ではないっ!

前回私はこう書きました。

「最低限文化的な関数型」の関数は第一級オブジェクト
  • 変数に代入できる
  • 関数の引数にできる
  • 関数を返す関数が書ける

総称関数は,この条件を満たしていないのです。

満たしているのであれば,次のようにも書けるはずですが,エラーになってしまいます。

let identity:<T>(T)->T = { x in
    return x
}

「関数は第1級オブジェクト」ということは,実体(instance)が存在するということですが,総称関数に実体はありません。クラスベースのオブジェクト指向言語におけるクラスに似ているといえば似ています。クラスはオブジェクトを生成するひな型ではあっても,オブジェクトそのものであるとは限らないという点において注1)。

これで,キーワードfuncがいらない子でないことが証明されました。定義はできても代入はできないSwiftの総称関数には欠かせないのです。

注1)
動的かつクラスベースのオブジェクト指向言語の場合,クラスはクラスで固有のインスタンスを持っていたりする点がややこしいですが。

著者プロフィール

小飼弾(こがいだん)

1969年生まれ,東京都出身。元ライブドア取締役の肩書きよりも,最近はPokemon GOのガチトレーナーのほうが有名になりつつある……かもしれない永遠のエンジニアオヤジ。

活躍の場はIT業界だけでなく,サブカルからアカデミックまで多方面にわたり,ネットからの情報発信は気の向くまま毎日毎秒! https://twitter.com/dankogai,ニコニコチャンネルは,http://ch.nicovideo.jp/dankogai,blogはhttp://blog.livedoor.jp/dankogai/

当社刊行書籍は『小飼弾のアルファギークに逢ってきた』『小飼弾のコードなエッセイ』など。他にも著書多数。

コメント

コメントの記入