Swiftポエム — null安全編
null安全 Advent Calendar 2016の12月1日の記事です。
先日話題になったnull安全系の記事は、本記事最後に貼ってある「関連記事」など読みましたが、「null安全であることは良いことずくめ」ということに特に異論無いです(厳密に見るとわずかなトレードオフもあるとは思いますが)。
その上で、今Swiftメインで、これまで色々な言語触ってきた上で感じていることについて、細かい技術的なことなどはあまり触れずに、僕の感覚的な観点中心に書いていきます。
Swift
Swiftが好きな思いは大体この去年のAdvent CalendarのSwiftポエム(特に「型が強い」の項)に書きました。
ミスったところは大体コンパイルエラーにしてくれてます。
Scalaなどと同様、関数型言語の性質も取り入れた書き方も出来ますし、かといっていわゆる関数型言語みたいな堅い縛りもなく、共存しやすいです。
一時期Haskell入門してモナドなど勉強して挫折気味でしたが、Swiftでそれ系の記事読んだり触れたりして理解深まりました。
SwiftでOptionalというnull安全のための言語仕様があることはもちろん嬉しいですが、個人的にはそれはそこまで重要では無いと思っていて、「型が強い」ことの一要素程度に思っています。
静的言語 vs 動的言語
僕は静的言語大好きで、動的言語で数ファイル以上にまたがるようなプログラムを書くのは避けたいと思っています。
動的言語では以下のあたりを強く不満に思っています。
- 静的言語ではコンパイルエラーで防いでくれるレベルのミスの発見が実行時まで遅れる
- テストのコードカバレッジを100%近くすればそういうミスは大体防げるが、そのために書きたく無い(個人的にはテストはロジックの正確性を担保するために局所的に書く派)のと、書いたとしてもテスト走らせるよりビルド時に検出できた方が効率良い
- コード補完があまり効かない(IDEでがんばっても正確な補完が常に出ることは原理的に不可能(でも型ヒントなどあれば出来そう))
- リファクタリング機能が貧弱
静的言語ではコンパイルエラーで防いでくれるレベルのミスの発見が実行時まで遅れる
null安全か否か、も大体この範疇ではありますが、それ以前に例えばString型とInt型を取り違える、みたいな静的言語ではありえないミスも頻発してしまい(スコープが小さければさすがになかなか起こらないが、複数ファイルにまたがると僕の場合は容易に起こる)、これがとても不満です。つまり、個人的には静的言語であることが重要で、null安全であることはそれに越したことは無い、程度に思っています。
リファクタリング機能が貧弱
ちなみに現在、Xcode 8.1 + Swift 3.0.1でも、グローバルにリネームしようとするとこの表示が出て、リファクタリング機能がかなり貧弱です。
ただ、これはSwift自体が悪いのではなく、Xcodeの支援機能がまだ追いついていないせいです。AppCodeを使えばリファクタリング出来ますし、拡張機能自作でも解決できるかもしれません。Swift 4くらいで言語本体の開発が一段落つけば、こういうところにも開発リソースを割かれて対応されるかなと期待しています👀
というわけで、null安全云々以前に、静的言語が主流になってほしい(ごく小規模なプログラムはどちらでも良いですが)と思っています。
ちなみに、元記事では動的言語で静的解析によってnullかどうかチェック・指摘するPythonなどもnull安全とみなしていますが、その静的解析で上に挙げた動的言語に対する僕の不満が充分解消されていれば、それでも良いと思っています。
動的言語のメリットもあるとは思うものの…
なんだかんだ、ちょっとした自動化スクリプトを書いて、サクッと実行したりどこかに配置する時など、取り回ししやすくて良いとは思っています。
動的言語の方がシンプルに書けるというメリットは希薄になってきた気がする
以前は動的言語の方が静的言語より簡潔に書けて書き心地良く(コード補完があまり効かないので個人的には書き心地悪いですが)、コード量も減る傾向にありましたが、Swiftなど最近の良く出来た静的言語と比べるとその優位性はかなり小さくなってきたと感じています。なので、コマンドラインツール用途でもSwiftをメインで使っていこうかなと思い始めています。まだまだクロスプラットフォームに関しては発展途中なので、同じく静的言語のGo併用したりでないとキツい場面も多そうだなとは思っていますが。また、ライブラリの充実度などの観点でPython(動的言語だが3.5の型ヒントなど使えば許容範囲かも)などが適した場面もあると思っています。
null安全への私感
ここから、null安全に対して思っていること中心に書いていきます。ようやく本題ぽくなってきます( ´・‿・`)
null安全では無い静的言語で書く際、nullに起因するクラッシュが支配的とは感じ無い
体感ですが、null安全で無い静的言語でこれまで書いてきた感じだとnullに起因するクラッシュはクラッシュ全体の1割も無い程度だったと思っています。まっとうな書き方をしていれば、nullに起因するバグでポコポコクラッシュすることはあまり無いです。繰り返しになりますが、null安全であるに越したことは無いと思っています。
Swiftのnull安全機構であるOptionalにてすべて解決、というわけでもないと思う
僕は「nullに起因するクラッシュ・ミスをゼロにしたい」、というよりそれを「早期発見したい」、という姿勢でいるべきだと思っています。ビルド時にコンパイルエラーとして検出出来ればベストで、これにnull安全機構であるOptionalは大きく貢献しています。
ただ、コードの書き方次第で良くない事態を引き起こしてしまうことは、言語仕様だけでは防ぎきれません。例えば、僕は次のようにあえてクラッシュするようなコード(someMethod2・3)を書くことがよくあります(あるいは例外投げるなど)。
(「そもそも設計がおかしい」と言われそうですが短いサンプルコードなのでご容赦ください🙇)
「とりあえずif let・guard・オプショナルチェーンでぬるぽ避ければOK👌」みたいな感じで何も考えずに想定外のフローの時もクラッシュを避けるために握りつぶしてバグの発生箇所特定が困難になりそうな書き方(someMethod1・4)をしているコードをたまに見ます(もちろん、本当に「nilの時は何もしない」ことを意図しているならまさにそのための書き方であり、まったく問題無いです)。わざとクラッシュなどさせるようにしていれば、その呼び出し箇所など少し追えば容易に原因を特定出来ることが多いですが、誤った使い方(意図せずnullが入っているケースのもみ消しなど)をした場合は、Javaなどで悪手として有名な「例外もみ消し」と同罪で、「nullに起因するクラッシュ」より悪質だと思っています。
Xcode上で、Optionalの変数に対してメソッド呼び出ししようとすると勝手に ?
を挿入して「null安全」にしようとするお節介も、あまり良くないと思っています(Java + IDEで例外もみ消しコード促されることがあるのと同様)。勝手に ?
を挿入せずに、普通にコンパイルエラーとして、開発者に適切なアンラップ方法を選択させるのが健全に思います。
Objective-Cでもnilに対してメソッド呼び出しするとクラッシュせずにスルーされますが、この挙動も同じ理由で問題であって、これが原因でかなりデバッグに苦労させられた思い出があります。(さらに、 nil
は適当に扱ってもクラッシュしないことに油断して NSNULL
の扱いにミスってクラッシュなども…。)
というわけで、Swiftにnull安全機構であるOptionalがあるといっても、本当にコードが良くなるかは書き手に依存する部分も多いなと感じています。ほとんど誰もやらないだろうと思える程度の誤用なら良いですが、僕はいわゆる「例外もみ消し」と同じくらい起こりえそうと懸念しています。
null安全で無い言語でも次のように null
である時のことをまったく考えずに安易に早期returnする処理など書いたら同じことではありますが、Optionalで「安全」になると「ぬるぽのもみ消し」もしやすくなりそう(慣れていない開発者がうっかりやりやすくなりそう)という印象です。
if value == null {
return
}
もちろん、うまく扱えば恩恵は大きいので、SwiftにOptionalの仕様があることに感謝しながら使っています。
最後に
null安全系の記事に対するちょっとした違和感
動的言語が、特にベンチャー界隈のサーバーサイドでまだまだ多く使われていて、僕はそれが一番課題に感じています。今、プロジェクトの都合で、サーバーサイドを動的言語で開発していて、SwiftでiOSアプリを快適に書けるのと比べて大きな不満を持っているという個人的事情もありますが。
サーバーサイドSwiftが今後伸びていって周辺環境も充実していき、問題無く採用できるような状態になれば一番嬉しい(現段階でSwiftでちょっとしたサーバーサイドコードは充分書けると思っているが、プロダクション導入はまだ躊躇中)ですが、例えば現時点でnull安全ではないC#でサーバーサイドを書くなどでも、僕の今の不満は大きく解消されます。
「動的言語嫌いなら好きな静的言語使えば良いじゃん」と言われそうですが、すでに動的言語で組まれているプロジェクトを静的言語に書き換えるのは多大なコストがかかるので、現実的で無いことが多いです(少しずつ移行というのも同じくコスト大)。また新規プロジェクトだとしても、特にベンチャー界隈での技術者集めやすさなど考慮すると、言語として自分が優れていると感じているものが必ずしもベターとはいかなかったりもします(とはいえ新規プロジェクトだったら静的言語を推進しますが)。
というわけで、動的言語でnullどころかあらゆる実行時エラーに苦しめられるようなことも多い現状、null安全にフォーカスして、null安全でない言語をレガシーというのはちょっと先走り過ぎかも?と思ってしまいました。そのあたりすっ飛ばしてnull安全にフォーカスしてしまっため、本来啓蒙したい人に良い具合に行き届かなかった気がしました(Twitterやはてブコメント見た感想)。
とはいえ、null安全であることはとても良いことと思っています( ´・‿・`)自分の普段扱う言語が静的言語であれば幸せですし、null安全だとさらに幸せです( ´・‿・`)