久しぶりのコミュニティイベントを開催しました
夏のKotlin LT祭と題して、
日本Kotlinユーザーグループ(JKUG)主催でLTイベントを行いました。
会場を提供していただいたDeNAさん、ありがとうございました!!!!!!
夏のKotlin LT祭と題して、
日本Kotlinユーザーグループ(JKUG)主催でLTイベントを行いました。
会場を提供していただいたDeNAさん、ありがとうございました!!!!!!
ってなんとなく思った。
いろんな言語を学びたいとか言いつつ、お前が真っ先に書こうとするのはJavaだろう、と。
「Javaが凄い出来ます!得意です!」とかなかなかそういうことを言う気にはならないんだけど、
パッと何かをしようとしたときには一番楽な言語となっている。それかKotlinなんだけど。
多分、Javaに関して言えば、開発環境がすでに目の前にあるから咄嗟に使いやすいというのは大きい。
例えば普段鉛筆を持たない左手で字を書いてみる。
書いているうちに左手で字を書くことを覚える、みたいな作業は必ず必要である。少なくとも僕の場合。
ただ、忙しい時にtryすると、
「この忙しいのになに悠長なことしてんだ」ということになるので時間の捻出もある程度必要になりそう。仕事ならば。
家で趣味コードであればダラダラ書いても良いかもしれないが。
やっぱある程度家で素振りしてから実戦で個人で使ってみるという流れかな。
それでよっぽどチームで使ってメリットがあるならチームで導入という流れになるだろう。と思う。
ホントにそれってJavaでやる方が向いている作業?ってこともある。
もしかしたらPythonの方が良いかもしれないし、JavaScriptの方が良いかもしれないし、
RustとかGoの方が良いのかもしれないみたいなの。
それでも案外Javaで出来てしまったりするからタチが悪いというか。
極端な話Semicolonless Javaみたいなことになる。
まぁそもそもそんな難しいことを僕自身が書いていない。
やっぱり素振りだなぁ。もっとコード書かないと
つまりこういうめんどくささがあります。
val f1 : () -> Unit = {}
f1() f.invoke() // OK
val f2 : (() -> Unit)? = null
f2() //コンパイルエラー
f2?.invoke() // OK
f1()という関数呼び出しは、実は演算子オーバーロードという機能によって実現しています。
f1()とコードを書いた時 f.invoke() が呼び出されるというものです。
f1は () -> Unit 、つまり引数なしでUnitを返す関数型をlambdaで表現したものですが
実体の型はFunction0<Unit> というインターフェースです。
Function0の実装はこうなっています。
// kotlin.jvm.functions
// Functions.kt
public interface Function0 <out R > : Function<R> {
/* Invokes the function. */
public operator fun invoke(): R
}
f1はFunction0自身であり、invokeが実行されます。
これがf2のようにnullableとなった場合にKotlinの演算子オーバーロードはそれを許しません。
f2?.invokeという風に厳密にかけとコンパイルエラーになります。
つまり、nullableな変数の関数呼び出し(nullableな型をレシーバーとする関数呼び出し)をしにくいということです。
前述したコードのf2を、f2?()とかって実行出来れば良いのではという感じもしますが、
Kotlinはそういった言語設計を行っていないようです。
f2?()という呼び出しをサポートする代わりに明示的にf2?.invoke()と書きなさい、としています。
これはnull安全的観点で言うと良いような気もしますが、
突然普段は演算子オーバーロードによって隠蔽されているinvokeという謎関数が出てきた感じになるので、実装者としては混乱するかもしれません。
推測ですが、f2?()とかという書き方をサポートしなかったのは
単純に?を使った記法を利用できる箇所を限定的にしたかったのと、
コンパイラの字句解析とかを簡単にしたい(めんどい)というのがあったのではないかと思います。
nullableだと演算子オーバーロードが利用できないかというとそうではありません。
拡張関数を利用すれば良いのです。
たとえば次のように実装すればinvokeの呼び出しも演算子オーバーロードを介して利用可能です。
operator fun <R> Function0<R> ?.invoke() = this?.invoke()
val f2 : (() -> Unit)? = null
f2() // コンパイル通る
ただし、Kotlinの標準ライブラリとしてわざわざこういった機能を提供せず
f2?.invoke()と書かせているのは、関数を実行したつもりで関数を実行していないなどのバグの元になるのを防ぐといった意図がありそうですしあまりお勧め出来るソリューションではありません。
もう一つ、理由として考えるられるのは
nullableなものを実行した時の対処というのは大体においてコーディングしている側に委ねられるものだと思います。
たとえばBigDecimal + BigDecimal で返されるのは2つのインスタンスの値を加算したものであるのは自明という感じがしますが
BigDecimal? + BigDecimal
BigDecimal? + BigDecimal?
などは何を返すかはちょっと考える必要があります。
あと、単純に標準ライブラリが膨らみますね。
悪ふざけコードです。
https://gist.github.com/yyYank/02d6c7f64532bb30cd0e63563d90feb2