書いて覚えるSwift入門

第47回 Swiftに足りないものは何か?

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

リスト1のコードにchurchNum.hsと名付けて保存します。

リスト1 churchNum.hs

zero  = \f x -> x
one   = \f x -> f x
two   = \f x -> f $ f x
three = \f x -> f $ f $ f x

succ  = \n f x -> f $ n f x
add   = \m n f x -> m f $ n f x
mul   = \m n f -> m $ n f

次にHaskellのデファクト標準実装であるghciで実行してみます。

% ghci
GHCi, version 8.4.3: http://www.haskell.org/ghc/:? for help
Prelude> :l churchNum.hs
[1 of 1] Compiling Main             ( churchNum.hs, interpreted )
Ok, one module loaded.
*Main> (add two three) (+1) 0
5
*Main> (mul two three) (+1) 0
6

これはチャーチ数(Church numerals)と言って,整数nは初期値xに関数fn回かける関数として定義するもので,ソースコードというデータをプログラムとして実行するチューリングマシーン(turing machine⁠⁠,つまり一般的なデジタルコンピュータとは逆に,関数というプログラムでデータを表現する手法です。詳しくはラムダ計算で検索してもらうとして,実際「twoとthreeをadd」した結果の関数に,初期値0+1する関数を入れると6になっています。⁠twoとthreeをadd」した結果も総称関数であることは,

*Main> (add two three) ((++) "!") ""
"!!!!!"
*Main> (mul two three) ((++) "!") ""
"!!!!!!"

として容易に確認できますし,その型がHaskellでは総称的に推論されていることも次のように確認できます。

*Main> :t add
add :: (t1 -> t2 -> t3) -> (t1 -> t4 -> t2) -> t1 -> t4 -> t3
*Main> :t mul
mul :: (t1 -> t2) -> (t3 -> t1) -> t3 -> t2

当然のことながら,チャーチ数の実装は動的型言語でも容易です。リスト2はJavaScriptの例です。

リスト2 チャーチ数の実装(JavaScript)

onst zero  = f => x => x;
const one   = f => x => f(x);
const two   = f => x => f(f(x));
const three = f => x => f(f(f(x)));

const succ =  n => f => x => f(n(f)(x));
const add  =  m => n => f => x => n(f)(m(f)(x));
const mul  =  m => n => f => x => n(m(f))(x);

console.log(add(two)(three)(x=>x+1)(0));    // 5
console.log(mul(two)(three)(x=>x+1)(0));    // 6

それではSwiftではどうでしょう? リスト3の結果をご覧ください。

リスト3 Swiftでのコード

typealias F<A,R> = (A)->R
typealias C<N> = F<N,N>
func zero<N> (_ f:@escaping C<N>)->C<N> {
    return {x in x}
}
func one<N> (_ f:@escaping C<N>)->C<N> {
    return {x in f(x)}
}
func two<N> (_ f:@escaping C<N>)->C<N> {
    return {x in f(f(x))}
}
func three<N>(_ f:@escaping C<N>)->C<N> {
    return {x in f(f(f(x)))}
}
// SUCC := λn.λf.λx.f (n f x)
func succ<S,Z,N>(
    _ n: @escaping (@escaping F<S,N>)->F<Z,S>
    )-> (@escaping F<S,N>)->F<Z,N>
{
    return {f in {x in f(n(f)(x))}}
}
// ADD := λm.λn.λf.λx.m f (n f x)
func add<S,Z,N>(
    _ m: @escaping F<Z,F<S,N>>
    )-> (@escaping F<Z,F<N,S>>)->F<Z,C<N>>
{
    return {n in {f in {x in m(f)(n(f)(x))}}}
}
// MUL := λm.λn.λf.m (n f)
func mul<S,Z,N>(
    _ m: @escaping F<S,Z>
    )-> (@escaping F<N,S>)->F<N,Z>
{
return {n in {f in m(n(f))}}
}

print( mul(two)(three)({x in x+1})(0) )
print( add(two)(three)({x in x+1})(0) )

ずいぶんとまだるっこしいものになってしまいました。実はこれ,昔のSwiftではもう少し楽に書けました。昔は@escapingは不要だったのです。その一方typealiasで総称型が使えるようになったので,3歩下がって2歩進んだと言ったところでしょうか。

Swiftの物語はこれからだ!

ひねくれた言い方をすると,Swiftは後出しじゃんけん言語です。ほかの言語の失敗に学び,いいとこどりばかりしてきたという点で。しかし後出しなのに負けている点も,上記のとおり見受けられます。

とはいえSwiftはまだ5才。ほかの主要言語は主要言語とみなされるまで10年以上かかるのが普通です。拡張の余地もまだあります。''(シングルクォート)はいまだに丸々未使用ですし,Ownershipも後付け不可能ではありません。

そもそも万能言語が必要かという疑問もあります。Cはなんでも書けますが,なんでもCで書く人はほとんどいません。何でもできるものは往々にして何でも不得意なもので,年々その数を減らしている自然言語とは逆に,プログラミング言語の数が増えているのは適材適所をユーザが求め続けてきた結果でもあるでしょう。

それでもなお万能言語を希求してしまうのもまた,プログラマの性でもあります。誕生して以来,Swiftはその候補としてずっといい位置にいますし,ユースケースを着実に広げています。Swiftはまだ始まったばかりなのです。

Software Design

本誌最新号をチェック!
Software Design 2020年12月号

2020年11月18日発売
B5判/184ページ
定価(本体1,220円+税)

  • 第1特集
    Dockerアプリケーション開発実践ガイド
    設計・データ永続化・セキュリティなどコンテナの定石を身につけよう
  • 第2特集
    設計に役立つAWSシステム構成図の読み方
    AZ,VPCなどの概念と主要なサービスを再整理
  • 一般記事
    MySQL Database Serviceの全貌
    【前編】MySQL Database Service利用ガイド
  • 短期連載
    Linuxカーネルの最強トレースツール「eBPF」を体感
    【3】eBPFでトレーシングツールを作ろう
  • 特別付録
    ちょうぜつエンジニア
    めもりーちゃんステッカー

著者プロフィール

小飼弾(こがいだん)

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

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

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