https://youtu.be/msmT9x76uMY
今回は、The Basics の導入部の残りのところ「型安全」について眺めてから、続いてその次の 定数と変数
についての節に入っていく予定です。変数や定数の特徴や定義方法といった Swift の中でも特に初歩的な話の回になりますけれど、改めて入門書に目を通しながらそこから今だと何が窺えてくるのか、そんな気持ちでおさらいしようと思ってますので、どうぞよろしくお願いしますね。
———————————————————————————— 熊谷さんのやさしい Swift 勉強会 #85
00:00 開始
01:04 型安全が主流?
02:52 NULL 安全ではない言語はレガシーな言語?
07:11 NULL 安全だと何が嬉しいのか
07:51 NULL チェックに伴うキャスト
09:42 タグ付き共用体
12:33 質疑応答
12:40 エラー発生箇所の明瞭性
14:20 Objective-C 好きについて
16:08 言語の先進機能について
18:10 スマートキャストの動作について
20:37 NULL 許容レシーバー
26:41 強制アンラップ属性
34:09 ふたつの型を受け入れられる文法
35:46 if let
shorthand
41:43 次回の展望
————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #85
はい、では改めまして始めていきますね。今日は「The Swift Programming Language」のベーシックス、その冒頭部分の話です。ちょっと振り返ってみたところ、「The Basics」という章に入り、その概要について話をするようです。今回の範囲の一番最後は「型安全」という文節です。そこをざっくりと見てから、次のセクションの「定数と変数」に進んでいこうと思います。まずはそんな流れで「型安全」について見ていく感じです。
スライドのほうを見ていくと、Swiftは型安全な言語であると書かれています。今どき型安全な言語は当たり前になってきたと思います。ただ、例外としてPHPなど、型安全ではない有名な言語も確かにあります。しかし、型安全が主流になっているのは間違いありません。
最近、コンカレンシーに関する記事を読んでいたら、「Swiftは他の言語とは違って、型安全な言語だ」という記述がありました。それを見たとき、「他の言語と違って」という表現はもうそろそろ使えないのではないかという印象を持ちました。10年も経てば当然のことですよね。ヌル安全も同様に、レガシーな感じになりつつあるんです。
ただ、ヌル安全の実現が難しい言語も存在します。例えばJava言語や、C言語、C++などです。これについて、ちょっと興味深い記事を見つけましたので、シェアしたいと思います。少々煽り感があるタイトルですが、内容は興味深そうですので、見ていきましょう。
コーヒーさん(記事の著者)は煽るタイプの人ではないので、きっと内容も楽しめるはずです。もし煽ってたらどうしようかとも思いますが、それも含めて見ていきましょう。
この記事は2016年11月に投稿されています。すでにその時点でレガシーとされているのが面白いですね。その頃からヌル安全は急速に浸透した感じがします。
例として挙げられている言語には、Python、Crystal、Flow、Hack、Kotlin、Rust、Swift、TypeScript などがあります。どれも名前を聞いたことがある言語ですね。
特にSwiftはObjective-Cの代わりを目指しており、TypeScriptはJavaScriptの代替としてよく取り上げられます。PHPに対するHackのような動きが多くの言語で見られます。
ヌル安全は、ガベージコレクションやメモリ管理の進化と共に、プログラミング言語における大きな進歩とされています。型安全でない言語からヌル安全な言語に移行すると、その便利さから元に戻ることが考えられないと感じる人も多いです。
メモリ管理やヌルの管理が手動で行われるのが好きな人もいるかもしれませんが、チーム開発や大規模プロジェクトでは、ヌル安全や型安全が万全を期する手段となります。
この記事は非常に長いので、すべて読むと大変ですが、要点を抑えて進みたいと思います。
ヌル安全とは何かについては、重要なポイントをスライドにまとめてあります。ヌル安全だと何が嬉しいかについては、早期発見ができることが大きなメリットです。特にSwiftのオプショナルはその点で非常に優れています。
では次に、「定数と変数」のセクションに進んでいきます。 そうですね。Swiftの特性や生産性を高めるための仕組みについて話していましたね。Swiftには、多くの便利な演算子があり、それらをうまく利用することでコードの生産性が向上します。また、スマートキャストという概念についても触れていました。IF文でヌルチェックを行うと、その変数が非オプショナルな型として扱えるというものです。この点はSwiftのオプショナルバインディングに似ています。シャドーイングを利用することで、IF文の中で変数がオプショナルではない形で扱えるようになります。
Google Apps Scriptも同様の仕組みを持っているという話がありましたが、詳細は忘れてしまったようです。この部分に関しては、もう少し検証が必要かもしれません。
オプショナルの扱いに関連して、強制アンラップやフラットマップといった話題も出てきました。ヌルアブル(Optional)についての特性やネストの可否についても興味深いですね。Swiftではオプショナルがネストできるため、非常に柔軟な表現が可能です。
タグドユニオンについても言及しましたね。Swiftのオプショナルは「タグドユニオン」として実装されています。この言葉の定義については、「Maybe型」とも比較されました。他の言語、例えばKotlinでは、二重はてな(??
)の利用が制限されていますが、Swiftではそのような制約はありません。この点がSwiftの優位性とも言えます。
コメント部分についても触れられていますね。検査例外(checked exceptions)の話題になり、C++の例外処理との違いについて議論していました。
Swiftは、安全性や生産性を高めるための多くの特徴を備えていて、その詳細を知ることで、より快適に開発を進めることができるでしょう。 全然分からない。うんうん、確かに確かに。それは欲しいですね。Kotlinとかもそういう考え方にはなっていないんですね。なるほど、Kotlinは多分Javaの影響を受けて、なるべく遅延しないようにという方針がありますけど、問題は残っていますね。
なるほど、それも欲しいですね。ヌル安全(Null Safety)は重要な機能です。そう考えると、TypeScriptはJavaScriptには戻れないと思うくらい良い感じなんですね。JavaScriptは結構苦労しますよね。うん、時々ハマることがありますし、演算が奇妙なことになることもありますね。
車好きな人が「マニュアルトランスミッション(MT)じゃないと運転しない」という話も理解できます。まぁ、そういった好みもありますが、私はObjective-Cも好きですよ。
そうか、MT車は楽しいんですか?自分はAT限定免許しか持っていないので、MT車に乗ったことがないんですけど。普段使いの車がMT車なんですね。結構楽しいんですか?
ああ、楽しいんですね。MT車は状況に応じてシフトレバーを操作するのが楽しいのでしょうね。自分はAT車なので、せいぜいエンジンブレーキを使うくらいしか楽しみはないんですが、それだけでも楽しいです。MT限定解除してみたら面白いかもしれませんね。
さて、プログラムの話に戻りますが、TypeScriptのany
を使ってJavaScriptとして使うのは、C++を使って手続き型しかやらないようなものですね。懐かしい話です。C++が浸透してきた頃は、C言語のコードを書いている人が結構いましたね。クラスを使わずにC++と言う人もいました。
あの頃から、オブジェクト指向が広まって、Swiftのように「オブジェクト指向は手に負えないからプロトコル指向に移行しよう」といった流れになってきたり、技術が進化する様子は面白いですよね。Null安全もそんな流れの一環かもしれませんし、新しい技術が出てきて手に負えなくなるというのも自然な進化かもしれません。
Kotlinのスマートキャストについても気になりますね。これはシャドーイングのようなものなのでしょうか。例えば、Swiftでスマートキャストができたとすると、value
がInt?
型のオプショナル型として、value != nil
ならばvalue
がInt
型として使えるということですよね。Kotlinではどうなるのでしょうか。代入すると一行目のvalue
が書き換わるのか、それとも暗黙的にシャドーイングされて影響しないのか。この辺りはまだ検証が必要ですね。 そういったところがすごく興味深く感じられますが、Kotlinのスマートキャストではどうだったでしょうか。確か僕はKotlin言語だけ触ったんですけど、挙動が違くてびっくりした記憶があります。今それを探しているんですが、ちょっとSwiftとは違う挙動で、ここでシャドーイングがあれって思った記憶があります。でもシャドーイングではないですね。インスタンスは同じはずなんですよ。
KotlinをJavaのVMのバイトコードにコンパイルして、それをJavaにデコンパイルすると、単純にヌルチェックしてるだけなんですね。なるほど、実装的には名前の違う別の変数とかではなく、全く同じものを扱っているんです。そのスコープではヌルではないことをコンパイラが保証してくれるという話ですね。なるほどね、なおさら面白い疑問というかバリューというか。
例えば何かバリューでも全然いいんですけど、コールバックではないな、何かがあったとして、ヌルではなかったらヌルを入れるよ、みたいな後始末をしたいとき。ポインターに対して、昔のメモリ管理ではポインターをフリーした後にゼロを入れて改めて使われちゃっても、ヌルポインターとしてちゃんと開放されたマークをC言語ではやってたわけですよ。C++でもそうですね。こういう書き方が、仮にスマートキャストという考え方が組み込まれているとできないですよね。どうなるんだろう。
いや、これはできると思いますよ。Pにヌルを代入する。はい、できますね。そしてこのPがオプショナル型のときですね。はい、そうです。そのifの中ではPがオプショナル型ではないからという話ですね。そうです、そうです。これがint型になっちゃうはずなので、ヌルって入らないですよね。ちょっと待ってください、やってみますね。
Kotlin公式のドキュメントをチャットに入りました。ヌル許容レシーバーですよね、多分。ヌル許容レシーバー。レシーバーって名前じゃないですかね。できますね、やっぱり。!= null
のスコープの中では、型としてはオプショナルですが、アンラップしなくてもいいという扱いになっていますね。
あー、なるほどね、そういう価値観か。なるほど、なるほど。IDEで見るとヌルオプショナルなんですが、アンラップしなくていいという扱いになっていますね。あー、面白いですね、それ。つまり強制アンラップに変わるということだ。
そうですね、ヌル型に自動キャストされる仕組みがあり、ヌルチェックの後だと自動キャストされるという言語仕様。言語仕様というか、構文解析とかそっちが頑張っているだけなんじゃないかな。チェックがなくなるか自動でキャストが入るかみたいな感じで。やっぱりJava互換というか、なので普通に言語仕様でオプショナルをちゃんと持っているというよりかは、ソースのパーサーがめっちゃ頑張って「ここはこうだよね」と制御している感じですね。
そうですね、須田浦さんのイメージは合っていると思います。オプショナルという型がちゃんと存在しているわけではないんです。なるほど。Javaと連携するとNon-null
にヌルを入れちゃうとかできちゃうので、ちゃんと気をつけなければいけないということですね。
つまり、Swiftが登場して逆輸入された色んなObjective-Cの機能があるじゃないですか。ヌルアブルとか。あれに近い感じですね。
そうですね、アノテーションですよね。なるほどね、面白いです。このスマートキャストすごくいいかもしれませんね。オプショナルバインディングだと完全にシャドーイングされちゃうじゃないですか。そうするとPにヌルを入れられなくなるんですよ。こうしたほうが好きですね。
電車だと3行目をコメントアウトしたら変数としてガチガチに決まっている電車は、フロー上より安全じゃないっていうか。Swiftの場合、どうせ最後同士のキーをヌルに戻したいんだったら、そもそも最初からif let
アンラップとPにすればいいんですよ。シャドーイングしないということですね。変数名を被らないように。
そうね、確かにその運用で全然問題なしですね。個人的にはこのスマートキャストが非常に魅力があって、可能性を感じるというか、両方あってもまずいいと思いますよね。 例えば Int
の強制アンラップ版のオプショナルってありますよね。この2つの違いっていうのは、明確なキーワードとしての属性はないですが、強制アンラップ属性のついたオプショナルという価値観で、フォースアンラップのオプショナル型が作られています。仮に、このあたりの仕様がちゃんとキーワードで例えば forcedUnwrapped
みたいなものがあったとします。そうすると、スマートキャストという価値観、つまりヌルチェックをしたときに、このブロック内では変数 P
がフォースアンラップ属性がついているものとみなす、というスコープになってくれるだけで全体がうまく動くようになります。
ここで、何かに渡したいときに、普段であればビックリマークをつけなきゃいけないというのが、さっきKotlinの話であった通りです。これが自動で強制アンラップ(フォースアンラップ)のフラグがついているから、といったことができるし、ヌル代入もできる。仮にこの後ヌル代入しちゃって、それをまた使ったらランタイムエラーで落ちる分には自分は構わないと思うし、このスタイルが綺麗な気がします。それでいてオプショナルバインディングも用意されていて、どっちも使える状況。これはコードの可能性や可読性が広がる気がします。
「熊谷さん、結構いろいろキワドイことをやりたいからじゃないですか?」と言われると、確かに納得しますね。そういうことか、確かに7行目あたりでちょっとキワドイことしていますね。これをKotlinでやると、8行目でコンパイルが通らないんですね。「あ、そうなんだ。コンパイルエラー?」と聞かれたので、「ヌル代入しちゃってるんで、スマートキャストが効かなくなりますね、8行目から。」と説明しました。素晴らしい解析だと思いますが、重そうですよね。でも、そんなに重くはないですし、ヌルチェックをしてリターンするような使い方が多いかもしれないですね。スマートキャストも、その4行目の if
でリターンを書いちゃって、それ以降はずっと p
はヌルじゃないという扱い、ガードレッと的な。そういう使い方の方が実際は多いかもしれないです。
なるほど、オプショナルバインディング不要で、従来のコードにヌルチェックの機能を拡張して使えるので、新しい構文を覚えなくても良いという利点はあります。ただ、その利点の一方で、スマートキャストを知らないと、なぜそうなるのかわからないという点もありますね。ヌルチェックが多いイメージがあるので、ヌルチェックをちゃんとしていれば、その後は安全に扱えるというのは確かにそうですね。スマートキャストをするなら var
じゃなく、やはり val
で宣言するのがセオリーかなと思います。
「val
っていうのがいわゆる let
でしたっけ?」そうですよ、と言いながら自分も言うつもりだったんですが、let
ですね。面白いですよね、話していて、他のみんなと価値観が自分とは違うところがあるのも面白い。この8行目はコンパイルエラーにならなくてもいいのではと思う自分もいますが、そのスコープ内では統一して P
はフォースアンラップのままでいてほしいという気がしました。でも、ランタイムエラーよりコンパイルタイムエラーの方が安全というのも確かですね。毛含めすぎず新しいヌル安全性を提供する言語が出て来る可能性もありますね。
あと昔の時代のObjective-Cの話に戻りますが、Objective-Cはヌルのまま進む形で、その安全性とは一応別の観点ですが、いろいろなニルの扱い方の価値観がある中で、いろいろな解決方法が生まれているのかなと。ニルアンハンドリングを言語側で解消する手法も様々ですね。
コメントを拾うと、TypeScriptに少し触ったことがある人がいますね。TypeScriptでは型(型のユニオン)を受け入れられる要素があるので、int | string
みたいな書き方ですね。その議論も含めて面白いですね。 C言語で言うと、ユニオン型っていうのがあったりするんですけど、そういうノリなのかな。C言語のユニオンはちょっと原始的すぎて、一つのメモリー空間をどっちの型と解釈するかみたいな、そういった感じだったような記憶があります。それよりはもうちょっと安全性に配慮されていると思います。Enamはね、そういったいろんなケースを想定できるから、特に関連値も持てるし、関連値を持つからそれが表現できるんですよね。便利ですよね。
あとはヌル許容レシーバー、さっきいろいろお話ししてくれてたやつか。あとはシンタックスシュガーが欲しい、あのif文の話ですね、さっきの。確かにそうね。あ、if let p
なんか話題に出てたやつですね。そうそう、このif let p = p
の11行目ね、今表示中の。この=p
を省略できる書き方があってもいいんじゃないかみたいな話がSwiftの方でも上がってて、まあ確かに自分はちょっとそこまでやらなくてもいいんじゃないかなみたいに思う方なんですけど、うん。シャドウィングとか分かりにくくなるし、特に値が完全に別の変数に入れちゃうとインスタンス振り返っちゃうんで、その時にね、if let p = p
みたいに明らかに代入文ってなってるのと、if let p
と言った時にそれが果たしてどうなってるのかが読めなくなってくるという心配が個人的にあって。
さっきのね、一番最初に今日スマートポイントじゃない、スマートキャストの話を聞いた時に、なんかこれどうなってんの?みたいな、p
は何なの?みたいな話。そういったのが出てきちゃって、で下手にlet
とかついてると代入してるように見えちゃうし、実際代入してるのかな?わかんないですけど。まあそういったのがね出てきちゃうから、なんか解釈が際どい。それならなんかスマートキャスト、4行目を導入しちゃった方が、なんか誤解なくていい可能性もありそうだなというのをね、ちょっと感じるんですよね。
で、ちょうどいいや、if let
の話。なんか話題に上がってたのがこれ、if let
shorthandっていうね。これが何だ?議論してるのかな、if let foo = foo
っていうのが気になってるらしくて、どこだ?どこに書いてあるんだ?あれどこだっけな?こういうやつ、こういうやつ。なんかこうやって代入させなくてもアンラップできるようにっていうね。こういったのがあったのが気になってたんですよね。ほんとだ259個もコメントが並んでる。ちゃんと読んでないけどどういう議論になってるんだろうね。でもこれをやるんだったらあくまでも自分はね、スマートキャストの方がスマートかなっていう、そういう感じがする。
あと他には、車の話全然わかんないね。ドリフトは聞いたことあるけれどESP切るっていうのが全然わかんないや。なんかの制御機構ですかね。あとはヌル、強化時ナッシングになり、面白い?とか、さっきのヌル入れるとっていうやつか、8行目の話ね。あとはなるほどね、クラス継承の中でcase is Bした時にその中ではBとして扱いたいよみたいなやつ。確かにね、is演算。ヌルをisでは判定できないからこれでいいんだ。同じノリね。例えば、めちゃくちゃですけど、B型だった時にはこの中ではBとして扱いたいみたいなね、サブクラスとして扱いたいみたいな、そうね。とかあとコメントに頂いた例えば、えーとここだ、if let p as B
。なるほどね、これはなんとなく綺麗に感じるのは、そっかスマートキャスト的な話か。Bとして扱っていくよみたいなね、そういう感じだ。なんか工夫次第で確かにもっとオプショナルバインディングよりも勝手がいいものっていうのは色々ありそうね。他の言語と比べながらSwiftらしいものみたいなのを見つけていって、ともすると実装してみたりすると面白いのかもしれないですね。
じゃあ時間的にもねちょうどいいので、これぐらいで今日は終わりにしますかね。じゃあまた引き続き、とりあえずヌル安全でお話ししてたので、また話すことがあればまた話すし、そうでなければ引き続きね、定数と変数の方に入っていくしみたいな感じでまた次回やっていきますね。はい、ではねこれで今日の勉強会終わりにしますね。お疲れ様でした。ありがとうございました。