Swift - 等しさとせつなさと型の強さと

[Swiftの関数の引数は、常に一つ]

> だとしたら、Swiftではカリー化は不要ということになってしまう。

[Swiftの関数の引数は、常に一つ]: http://qiita.com/dankogai/items/46fedc447dd93d1e0fbc

もちろん、そんなことはない。

求めているのは、「n-tupleを引数とする関数を、1-tupleづつ引数を受け取り、n個受け取った時点ではじめて値を返す関数にすること」である。

例えばこんな風に。

````swift
import Darwin // for log(Double)
func logWithBase(base:Double)->Double->Double {
    return { x in log(x) / log(base) }
}
````

````swift
let log10 = logWithBase(10)
log10(100) // 2.0
````

そうすること、つまり

````swift
func f<T0,T1,...Tn,R>(a0:T0,a1:T1,...,an:Tn)->R
````

を

````swift
func f<T0,T1,...Tn,R>(a0:T0)->T1->T2->...->Tn->R
````

にするのが、カリー化(currying)というわけである。言葉にするとまだるっこしいが、関数の宣言として書くと直感的にわかる。

## 任意の関数をcurry化するには?

それでは、任意の関数を受け取ってそれをカリー化する関数は書けるのだろうか?

2-tupleの関数であれば、あっさり書ける。

````swift
func curry<A,B,R>(f:(A,B)->R)->A->B->R {
    return {a in {b in f(a,b)}}
}
````

自分で定義した関数のみならず、ビルトインはおろか演算子関数さえ受け付ける。

````swift
let plusplus = curry(+)(1)
plusplus(41) // 42

````

3-tupleでも

````swift
func curry<A,B,C,R>(f:(A,B,C)->R)->A->B->C->R {
    return {a in {b in {c in f(a,b,c)}}}
}
````

4-tupleでも

````swift
func curry<A,B,C,D,R>(f:(A,B,C,D)->R)->A->B->C->D->R {
    return {a in {b in {c in {d in f(a,b,c,d)}}}}
}
````

しかし、任意のtuple長の関数を、たった一つの`curry()`で賄えるのか?

## 長さ変われば型変わる

ここでやっと本題に入れる。

できない、のである。

なぜか。

長さが変わると、型も変わってしまうから。

`[0,1]`と`[0,1,2]`はどちらも同じ`[Int]`型だが、`(0,1)`と`(0,1,2)`は前者が`(Int,Int)`型で後者が`(Int,Int,Int)`型。任意の型の関数を受け取るところまでは、「[Swiftの関数の引数は、常に一つ]」で示した通り`A->R`で行けるのだが、任意の型を返すのはジェネリクスをもってしても無理なのである。

これは、[Pythonのtuple()]のような、任意の`Sequence`から同じ長さのtupleを生成するような関数はSwiftでは実装できないことを意味する。

[Pythonのtuple()]: http://docs.python.jp/2/library/functions.html#tuple

もっとも実行時も変数が自分の型を覚えている動的型のPythonのtupleと、コンパイルされたら(どうしても必要な場合を除いて)型が外される、静的型のSwiftやScalaなどのtupleは、ずいぶんと意味が異なる。むしろ静的型の言語から見れば、Pythonのそれはtupleというより単なるimmutable listに過ぎず、実際各要素へのアクセスは、

````python
(0,0.0,"zero")[2] // "zero"
````

とlistとまるで変わらないのだし。

`AnyObject`であれば、Pythonと同様のことをObjective-C的に実装することも出来なくはなさそうであるが、SwiftにおいてAnyObjectへのキャストは意味がまるで異なるので、「静的型の要素からなる任意の長さのSequenceをtuple化すること」は「出来ない」といい切ってもよいだろう。なお、`Any`と`AnyObject`の違いについては、記事を改めてまた書く予定だ。

## [0,1] == [0,1] が出来て (0,1) == (0,1) が出来ないなんて

Tupleをサポートする他の静的言語を使い慣れた人が、一番びっくりするのはこれかも知れない。

![Screen Shot 2014-07-31 at 10.04.34 AM.png](https://qiita-image-store.s3.amazonaws.com/0/19227/dcae7d88-22c1-6ed7-d8a1-60bcc5d47c6f.png)

なんと、tupleどおしの等しさを確認できないのだ。

(要素が`Equatable`な)配列どおしの等しさなら確認できるのに。

もちろん、これはすっぴん状態で、ということであり、以下のようにすれば 2-tuple (Doubleと呼びたいところだが倍精度浮動点小数にもう抑えられている)が即座に比較可能になる。

````swift
@infix func ==<A:Equatable,B:Equatable>(lhs:(A,B),rhs:(A,B))->Bool {
    return lhs.0 == rhs.0 && lhs.1 == rhs.1
}
````

````swift
(0,1) == (0,1) // true as it should be
````

これをtupleの長さごとに書くのは正直めんどくさすぎる。

## せつなさの理由

とはいうものの、そうなっている理由を察せないわけでもない。

[組み合わせ爆発]だ。

[組み合わせ爆発]: https://www.youtube.com/watch?v=Q4gTV4r0zRs

Tupleの要素となりうる型がm種類あるとすると、要素数nまでのtupleの型の種類は、tupleのネストを認めなかった場合でさえ(m+1) ** nも存在することになる。

実際にはtupleの入れ子はSwiftでも認められている。そして`(T,T,T)`と`(T,(T,T))`と`(((T,T),T)`はそれぞれ別の型なのである。

> 型の増大を防ぐことができるのがメリットだと思うよ -  http://qiita.com/f81@github/items/a8419532c316d190782d

ずいぶんな誤解である。「型定義の増大」であれば話はわかるのであるが。

Tupleの最大長は(C#は8?)、Scalaが22、Haskellが64。ずいぶんと短いのは、これが原因ではないか。

ちなみに現時点においてSwiftではtupleの最大長は無制限なようである。無制限な代わりに、すっぴんでは`.0`,`.1`...と、各要素のgetterしか持たない(ラベル付きのtupleではそのラベルも使えるが)。extensibleでもないので、

````swift
extension (Int, Int) : Equatable {}
````

のようなことも出来ない。

そういうことをしたければ、

````swift
struct Point2d: Equatable {
    var x = 0.0, y = 0.0
}
@infix func ==(lhs:Point2d, rhs:Point2d)->Bool {
    return lhs.x == rhs.y && lhs.y == rhs.y
}
````

といった具合にきちんと型を作りなさいというメッセージなのだろうか。そして「任意の長さのtuple」であれば、代わりに配列を使えというメッセージなのだろうか…

(Dan, Swift, Newbie)