Swiftで代数学入門にいたく感銘を受けたので自分でもやってみようと思ったのだが型落ちのWindowsしかなくSwiftが動かない中Javaで少し実装したものの演算子オーバーロードがほしくてKotlinに手を出してみた。
あいかわらず他人の褌なのは笑って許せ、というか元記事様は代数学の実装という志高く勇気あふれる名エントリだがこちらはたんなるコピペ記事なので見知り置け。
群、環、体の実装まで
有理数までで満足してしまった。ダイアモンド継承になってる箇所があるけど定義上気にしない。
よく似ていると噂のSwiftとKotlnだけど、どちらも知らない俺がすらすら移植できたのでやはり似ているのでしょうね。以下は移植の際に「んっ?」てなったとこ。
protocol と interface
Swift ではプロトコルプログラミングが推奨の様子。protocol と struct で分離するのは筋がよさそう。Java でやるには interface と class となる。Kotlin には Object や data class があるが、まぁクラスでいいじゃん(タイプエイリアスがらみは後述)。
interface Ring: AdditiveGroup, Monoid {}
class Z(value: Int): Ring {
...
}
extension
Swift における extension は Java8 以降ならインターフェースのデフォルト実装で実現できる。
interface Group: Monoid {
...
// extension
fun testAssociativity(a: Group, b: Group, c: Group): Boolean {
return (a * b) * c == a * (b * c)
}
...
}
残念ながら、Kotlin(というか Java)ではインターフェースでのプロパティ初期化は許可されないので、実装側に追い出されるのであった(後述)。
演算子オーバーロード
Swift では演算子を直接指定してオーバーロードできる。標準で用意されている演算子以外も指定可能。
Kotlin ではあらかじめ定義されたメソッドのオーバーロードのみ可能。「*」なら「times」、「+」なら「plus」メソッドを指定する。
override operator fun times(other: Monoid): Z = this * other
override operator fun plus(other: AdditiveGroup): Z = this + other
Swift だとブレースと return 節が必要だけど、Kotlin では「=」で直接書ける。良い。
Self
Swift がいいなと思った一つ目が「Self」。シグニチャで使えるのってすごい良い。
Swift ならシグニチャに「Monoid」は出現しない。
protocol Monoid: Equatable {
static func *(a: Self, b: Self) -> Self
static var identity: Self {get}
}
Kotlin だと明示しなくちゃいけない。
interface Monoid {
operator fun times(other: Monoid): Monoid
var identity: Monoid
}
なので、実装側で下記のような型変換が必要になる。
override operator fun times(other: Monoid): Q {
other as Q
return Q(this.p * other.p, this.q * other.q)
}
むう。
type alias
Swift でもう一つうらやましいのが型別名。元記事では必要に応じて Int や Double を流用しているけど、現段階では Kotlin は型別名の指定ができない。ので、下記のように型を明示したフィールドを持つことになる。
class Z(val value: Int): Ring {}
元記事のように Intger の演算子を流用できないので演算子オーバーロード分はフル記述することになる。プロパティの初期化も実装側に追い出されるため、最終的な記述は下記の通りとなる。
class Z(val value: Int): Ring {
// alias
override operator fun times(other: Monoid): Z = this * other
override operator fun plus(other: AdditiveGroup): Z = this + other
override operator fun unaryMinus(): Z = -this
// extension
override var identity: Monoid = Z(1)
override var zero: AdditiveGroup = Z(0)
}
総論
いくつか Kotlin を dis ってるように書いたがとんでもない。Java 畑育ちからするとエレガントで直感的な良言語でした。並行して scala でも書こうとしたけど、そちらは形にならなんだ。個人的には数ある JVM 言語の中ではとびぬけてしっくりくる気持ちのよい感触だったことを強調する。
Swift は最初 Linux 上の REPL で触ってたけど、ちと制約がきつすぎてお勉強で触るにもまだ早いのう。