https://www.youtube.com/watch?v=HyIo3YEgTak
今回からいよいよ新しい題材! Apple 公式の Swift 言語解説書 The Swift Programming Language
を眺めていきますね。
この会は広く深く、丁寧に Swift を見ていく会になるのでいつからでも思い立ったときが参加の好機とは思うのですけれど。せっかくの新しく始まる機会ですので、もし気になってくれている人がいらしたらぜひ参加してみてくださいね。いつでも話に混ざってくれても良いですし、なんとなく聴いていてくれているだけでも歓迎中です。
———————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #23
00:00 開始 00:46 オリエンテーション 01:07 これまでの開催を振り返る 01:35 勉強会の題名について 02:34 勉強会のスタイル 03:29 この勉強会の目的 04:49 勉強会の方向性 06:13 勉強会の対象者 06:42 参加のしかたと心持ち 09:48 The Swift Programming Language 10:47 これからの展望 11:06 About Swift 11:30 Swift とは 12:35 安全、高速、対話型 15:04 REPL 15:40 初学者にやさしい言語 16:51 ありがちなエラーを起こらなくしている 17:27 変数は使う前に初期化が必要 19:24 真偽値を比較するとき 25:17 条件式に代入文を書くことについて 28:57 配列のインデックス検査 34:53 配列のインデックス範囲を検査する方法 38:53 整数のオーバーフローチェック 43:00 オーバーフロー演算子 46:53 nil やメモリー周りの扱い 47:59 エラーハンドリング 49:52 次回の展望 ————————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #23
今回から「The Swift Programming Language」に入っていきます。ずっとやってきた中で、新たに第2回目以降から参加された方もいらっしゃるので、初心に戻る意味も込めて、最初に軽くオリエンテーションを挟んでから本編に入りたいと思います。
まず、久しぶりに自己紹介をさせてください。私は熊谷と申します。この勉強会には Slack の OJT チャンネルがありますので、ぜひ遊びに来たり、コメントを書いていただけると嬉しいです。この勉強会は7月14日から始めておりまして、今日でおおよそ2ヶ月が経つことになります。この2ヶ月間で「About Swift」と「API デザインガイドライン」を見てきましたが、自分の中では非常に充実していたと感じています。
題名について少し振り返ってみると、最初は勝手に付けた名前でしたが、これまで参加してくださった皆さんはどう感じているでしょうか。「優しい」という言葉を聞いて、実際に優しいと感じましたか? もし何かご意見があればお聞かせいただきたいです。扱っている題材は優しいですが、話が優しいかどうかはわかりません。でも、それなりに有意義なことは話せているのかなと感じています。題名が話し手と聞き手を隔てていると振り返ると感じる部分もありますが、これはこれで良いのかなとも思っています。
初回のイメージとして、この勉強会はもともと株式会社ゆめみさんに招いていただいて始まったものです。翌日からいきなり「とりあえずやってみよう」という感じでスタートしましたが、みんなと一緒に作り上げていけたらいいなと思っています。2ヶ月通してスタイルが確立してきたようにも感じていますので、この回の雰囲気を大事にしていきたいと思います。
この回の大事な目的として、当初から挙げていたのは、プログラミング言語、とりわけ Swift の基礎学力を養うという点です。実際、そんな感じで進められてこれていると思います。国語的であまり派手さのない分野ではありますが、Yumemi さんの雰囲気がそういう基礎学力を高める内容を尊重してくれるのが嬉しいです。将来、難しいコードに出会ったときにもこの基礎学力が役立つと思いますので、Swift に親しむ場として活用していただければと思います。
今回から「The Swift Programming Language」、Apple の公式解説書を使って学習していく段階に入ります。これを見ていく中で広く深く学べるようにしていきます。たとえば、同じようなことでも他の視点から引っ張ってきたりしながら進めるので、一度間を空けてもついていけなくなることはないはずです。また、一回聞いただけでスキルが身につくわけではないので、繰り返し同じようなことを聞く機会があるかもしれませんが、それを活用しながらだんだんと身に馴染んでいっていただければと思います。
対象は主にアソシエイトエンジニアや Swift に関わる人ですが、関わっていなくても歓迎です。基礎的な部分をしっかり押さえることを大事にしていきます。
この回のもう一つの大事なポイントとして、談笑形式を採用しています。せっかく集まっているのだから、一緒に話しながら理解を深めたり、新しい発見をしたりしようというスタイルです。最初は物理的な勉強会を想定して、声で話しかけてくれることに重点を置いていましたが、Zoom だとチャットも使いやすいですし、テキストでのやり取りも活気が出てくると感じています。それぞれの良さを活かして、自分なりに声かけをしてもらえたらと思っています。 話しかけてもらうテキストでも、声でも話しかけてもらう上での大事な心持ちは、これから先も変わらないと思います。まず、どんなに詳しそうな人であっても、ほんの些細なことを知らなかったりするのは当たり前です。「これぐらい誰でも知ってるよね」と思って話しかけるのをやめてしまうのは非常にもったいないです。
話しかける上で自信がないことって、よくあると思うんですけど、それが正しいか間違っているかなんてものは、話していく中でいくらでも見つかっていくものです。そもそも正しいか正しくないかなんてものが存在しない話題もよくあります。そういったことを気にしていると話しかけられなくなってしまいます。
だから、「なんとなくこう思うんだけどね」ということも、ぜひぜひ遠慮なく話しかけてもらえたら嬉しいかなと思います。そして大事なこととして、誰もが誰かを騙すために話すということはありません。誰かが何か言ったとして、それに対して「なんか違うな」と思っても、その人にとってそれは正しいのです。逆に、自分にとって正しいと思っていることも、実は違うかもしれません。そんな風に、誰かの言葉を大切にして受け止めていけたら、いい感じに有意義な時間が過ごせるんじゃないかなと思います。
では、そんな感じで「The Swift Programming Language」の本編に移りましょうかね。
はい、今回からお待ちかねの「The Swift Programming Language」に入っていきます。アメリカ英語で書かれていますので、英語が苦手な人にとってはなかなか敷居が高そうに感じるかもしれませんが、読んでみると意外とシンプルな言葉で綴られていて、英語が苦手でも結構読みやすいです。さらには、DeepLみたいな翻訳アプリにまるっと投げてあげると、いい感じに返事が返ってきます。そんな感じで読み進めるといい参考書になります。個人的にはとても好きな参考書なので、題材として取り上げてみようと思いました。無料なので、そこも嬉しいですね。誰もが気軽に揃えられる、とても素晴らしい本だと思います。
それでは、その中からまずは「About Swift」と「バージョンコンパチビリティ」を読み進め、その後に「Swiftツアー」を見ていこうかと思います。「About Swift」は第2回目から見たことがあるかもしれませんが、「The Swift Programming Language」から見るとまた違った視点でSwiftの魅力が感じられると思います。その違いも楽しんでいただけたら嬉しいです。
まず、Swiftとは何かというところですが、ソフトウェアを書くための素晴らしい方法です。これは「The Swift Programming Language」に書いてある内容をほぼそのまま訳しているスライドですけど、素晴らしい方法でもあるSwiftに対して、「うん、素晴らしいね」と思えるのが特長かなと個人的には思います。
現在のところ、主にスマートフォンで活躍する言語というイメージが強いですが、Macアプリも書けますし、iOSをやっていると他のプラットフォームでもなんとか対応できるので、ぜひ試してみてほしいと思います。これはまだ先かもしれないですが、その他あらゆるプラットフォームでもコードを実行できる選択肢となる素晴らしい言語です。ただ、これはもう少し先の話かもしれません。
Swiftの特長としては、安全、高速、対話型という点が挙げられます。特にエンジニアの方々にとっては、全てがとてもありがたい機能ですよね。安全性は実際にコードを書いてみるとすごく実感できますし、高速さは気にしなくても相当早いです。対話型に関しては、この勉強会でもお世話になっているプレイグラウンドが充実しているおかげで、こういった勉強会が成り立つ。学力向上の意味でもとても良い感じの言語に仕上がっています。
また、最新の言語の考え方と、Appleのエンジニアリング文化、そしてオープンソースによる多大な貢献が集結している言語でもあるので、とても好奇心をくすぐる言語です。コンパイラーは実行性能に最適化され、言語自体は開発者に最適化されています。ここが特に好きなところです。もし性能だけが素晴らしいと言うならば、C言語やC++、はたまたRustなどが優れていますが、言語自体の開発のしやすさを考えると、Swiftは初心者でも書きやすい言語です。
プレイグラウンドの話もありましたが、REPL環境も説明しておきます。Swiftはコマンドラインからも手軽に動かせます。Xcodeのような大がかりなツールを使いたくない人にとっても、サクサクと動かせるところが魅力的です。
それでは、Swiftが初学者に優しい言語であるというお話について、もう少し詳しく見ていきましょう。 このあたりからどうしようかな。Playgroundで実際に見ながらのほうが楽しいかな。そうしてみますか?とりあえず、言語の最適化とコンパイラーの最適化によって産業用品質の言語でありながらも、スクリプト言語のような表現力と楽しさを備えている言語です。これが大事なポイントで、これからSwiftに関わるみたいな人はこのあたりをイメージすると、やっぱり楽しいなっていうのを実感しやすくなる気がします。ぜひここを頭の片隅に留めて、スクリプト言語のような表現力と楽しさ、このあたりを片隅に入れてコードを書いていくといいんじゃないかなと思います。
最新のプログラミングパターンを採用し、ありがちなコンパイルエラーを起こさなくしている。これは徐々に実感できていくことですね。そこで具体的にどういった例が挙げられるかっていうのが、このスライドの配分で列記したところです。これらがありがちなエラーを解消しているもの。
まず、変数は使う前に必ず初期化を必要とするというところ。ちょっとこのあたり、ザクザクッとPlaygroundで見てみましょうか。主にC言語とかからよくありがちなミスを解消するという着眼点でSwiftはできてるような気がします。要は初期化されていない変数を使うというのはどういうことかというと、Int型で変数を宣言するんですけど、それをうっかり値を設定する前に使おうとするとコンパイルエラーになるよというのがこの話です。
print
というと、ついついデバッグ出力みたいなイメージをするかもしれませんが、要は何かしらの関数に渡しちゃうとか、そういったことがありがちです。特にif
文とかでもし得られた乱数がtrue
だったときにはaに何か初期値を入れる。ちょっとこのコードだと明瞭な気がしないから、例えばfalse
だったときにしましょうか。false
だったようなときにはa
をゼロにする。それ以外のときには何かするつもりだったのに忘れちゃってたとき、こういったときにコンパイラーがちゃんと初期化前に使われるか、初期化前に使われる可能性がありますよと検出してエラーにしてくれる。これが大事なSwiftの特徴の一つです。
このコードを書いてて、ふと余談ですけど思い出しました。この==true
と==false
、特に==true
かな、これを書くか書かないか議論がありますよね。これは必要に応じてだと個人的には思います。どっちが正解というのはない。これは今まで話してきたデザインガイドラインにも通じるところがあります。このステートメント、この式を見て、これが過不足なくすべての言葉で表現できているかどうか、APIデザインガイドラインの基本原則ですよね。
これを見たときに、果たしてこのtrue
は過不足なく表現しているのか、過不足なく両方だから表現として足りないかまたは冗長か。他の場面でもうtrue
だって言ってるのに、ここでもまたtrue
というのかみたいな。というふうな判断で使ったらいいのかなと。
例えばこの今の例が多分何気なく書いたけど、なかなかいい例かなと思いますけど、乱数として得られた真偽値の値がtrue
であるかって読めますよね、このコードを見ると。だから、個人的にこれは==true
ってあるのはいい気がします。逆にif bool random
って書くと、「真偽値で取った乱数がそれであるか」ってしか書いてないじゃないですか。分かる人は分かるけど、もうちょっと補足してもいいんじゃないかなと思うんですよね。
これは例えば別のビルドとかだったりして、例えばここでUIKit
じゃない全然違うNSWindow
とかで何かインスタンスがあったとして、こんな書き方しても何の意味がないんですけど、それが透過的か、ウィンドウが透明かどうかっていうプロパティーを持ってて、それが着目されるべき時のコードなのに、通常のウィンドウだったらそれが必要ない。ウィンドウが透明かどうかっていうのを書くときに、ここでは==true
なんてわざわざ補足しなくても、透明性がtrue
であるかみたいな表現が自然にできるわけですよ。
もっと丁寧に言うと、「ウィンドウが透明であるかがtrue
であるか」となりますよね。だからここは言い訳して、「ウィンドウの透明性がtrue
であるか」としないと解釈が難しくなります。でもwindow.isOpaque
みたいに書くと、「ウィンドウが透明であるか」っていうのが素直に読めますよね。これが素直に読めるかっていうのも、APIデザインガイドラインのルールを返すプロパティやメソッドの名前の付け方っていうガイドラインがありましたよね。あれも同時に生きてて、それがあるからこその表現ともいえるんですけど、いろいろと見ていくとこの一文 if window.isOpaque
でスマートに表現できてしまいます。そういったところもSwiftが大事にしているポイントなんです。
ちょっと余談でしたけど、このようにいろいろ考えると==true
がいるのかいらないのか問題っていうのはいろんな見え方がしてくる面白いところかなって思います。 そうそう、補足してくれているように、しっかりと is
が Bool
を返す関数っていうコーディングルールが API デザインガイドラインにあります。まさにそれそれといった感じのことが、いろんな方面から組み合わさってとてもきれいな証言ができるようになっています。そういった言語になっています。こういうところがいいですよね。
ここで if flag == true
というマニアックな書き方に触れます。知らない人にとっては「何だろうか?」と思うかもしれませんが、意図をちゃんと自分で決めていれば問題ありません。これは文字面の話ではなくて動きの話ですね。等号演算子 ==
を癖で書いてしまうと、例えば C 言語をやっている人にとってはこのコードが書けるようになると、ちょっと一皮剥けたように感じたり、ある意味レベルアップを感じさせる書き方です。
そのため、ブール型の変数を使う場合は if flag
のように、明示的に == true
を書かない方が一般的です。これには評価式が評価されて、右辺に書いた評価式を評価して得られた値を左辺に代入するというプロセスがあります。そして、代入された左辺が最終的な条件式の結果として得られます。この結果が条件式として if
文に渡され、true だったらブロックが処理されるという非常に高度なプロセスがあります。これが C 言語やパール言語などで一般的だったのです。
Swift はこのような書き方を大胆に廃止しました。この言語仕様の決定は、昔からプログラミング言語に慣れ親しんでいる人にとっては魅力的なポイントでもあります。等号演算子 ==
を一つだけで表現する人が多く、それが誤動作を招くエラーを起こしやすいのですが、Swift はこれを排除しています。
また、配列のインデックスの境界エラーのチェックが行われるという特徴もあります。これによりメモリのオーバーフローが起こらない仕組みが用意されています。このとき、Swift ではランタイムエラーでそれを通知します。Swift に馴染んでくると、ランタイムエラーを積極的に起こすという印象はあまり持たないかもしれませんが、これは安全性を確保するための手段です。
具体的には、要素が三つしかない配列で四つ目の要素を取得しようとすると、ランタイムエラーが発生します。これも Swift が敢えて取っている安全性のための措置です。昔は、うっかり誤った値を代入してしまい、存在しない四番目のメモリ領域を書き換えてしまうことがありました。その結果、他の変数のメモリが書き換えられ、バッファオーバーフローが起こるということがよくありました。
また、コンパイラが初期化していない変数へのアクセスを防いでくれるという例もあります。こうした処理は、C 言語で言う未定義動作を撲滅するものです。この未定義動作の撲滅は、Swift が提供する非常に重要な保護機能の一部です。 この配列を超えたやつunchecked
っていうコンパイルオプション、確かにありましたね。そうそう、それだ。そのときに何か挙動が変わるんですよ。ただ、もしかすると、何かこのunchecked
っていうオプションの名前の響きからして、これはどっちかっていうと安全性を排除するとかそういうことではなく、コンパイラの実行性能に最適化する表現のほうに近いですね。より速度を出したいとき、例えば産業用品質のソフトウェアを目指したときに、ここはもうギリギリまで速度を出したいんだ、だから事前に徹底的にチェックを行って、ランタイムまでチェックを持ち越す必要がないっていう場合にunchecked
っていうオプションをつけて実行することで最大限のパフォーマンスを引き出せる、そういった目的にしているかもしれません。面白いですね、そういう見方をすると。
たとえば、未定義だった値を取るときの手法としては何種類かありますよね。具体例として、配列を定義するときにサブスクリプト(subscript
)を使って、自分で定義する場合があります。これでエレメントを返し、この中で値がなかったときにエラーを返すなどの方法で実装することができます。他にも、サブスクリプトのインデックスとして値を渡したときにこれをオプショナルとして返す方法も考えられます。値がなかったときにはnil
を返す方法や、エラーハンドリングをする方法が挙げられます。
たとえば、このようなコード内でエラーが起こるとインデックスアウトオブレンジのエラーが返されるという表現方法もあります。他にも方法がありますが、とりあえずこれらの3つをざっくりとイメージできると思います。この上2つの方法では何が起こりうるかというと、オプショナルを返す場合、それがnil
かどうかの判定処理を入れる必要があります。エラーハンドリングも同様に、エラーが返ってきたときにそれをキャッチする処理を挟まないといけません。そうすると、どうしてもパフォーマンスに支障をきたします。頻繁に使わないものに対しては問題ありませんが、配列のような基本的なデータ構造の場合、それが積み重なることによって致命的なパフォーマンス低下になる可能性が高いです。
そのため、事前条件として条件を満たさなかったときには即座にエラーを返し、それ以上のレベルでのソフトウェア的な制御を省略するパフォーマンス改善もあります。そして、unchecked
のようにプレコンディション自体を無視してしまおうという方法もあります。実際にコードを書くときには、このようなことをイメージして、どれが最も今の場面で求められるかを判断する必要があります。
また、整数のオーバーフローチェックが行われるかどうかにも関連します。例えば、数字があってこれがカウントダウンするようなプログラムを考えます。以下のようにfor
ループを使って、10からカウントダウンしていくコードを書いたとします:
for i in (0...10).reversed() {
print(i)
}
このように、カウントダウンが行われます。それが、誤って符号なし整数(UInt
)を使ってしまうと、負の値をとるときにオーバーフローが起こり、無限ループになる可能性があります。このようなことが起こらないよう、十分に気をつける必要があります。
これは、41行目と43行目に示したような近くにある場合ならすぐ修正できますが、遠いところや他のファイルにある場合には、誤ってUInt
がかっこいいと思って使ってしまうと大変なことになります。 そういった点で、このオーバーフローチェックが入っていることによって、Swiftはランタイムエラーで止めてくれる。それが素晴らしいところです。まあ、どれくらい共感してくれる人がいるか分からないですが、この話自体が非常に重要です。
さて、さっき書きましたけど、&
を使うことでオーバーフローが発生したときにランタイムエラーを発生させる仕様になっています。この仕様は、実行のパフォーマンスを意識したときに非常に役立ちます。&
を使うことで、「絶対にオーバーフローが起こらないんだから」というときにチェックを飛ばすことができるわけです。
話しててふと疑問に思ったのですが、これアンチェックオプションをつけたときにどう動くのでしょうかね?チェックを外してくれるのでしょうか?もしチェックを外してくれるなら、この書き方は素晴らしいと思います。ただ、もしチェックを外してくれないのだとしたら、パフォーマンスを気にして&
をつけるべきかつけないべきかを上級者は配慮する必要が出てくるでしょう。
特に四則演算などは原始的な数値型に関わることが多いでしょう。ループが100万回とかになると、チェック量も無視できなくなってきます。その辺り、オーバーフローするはずのない部分を意識して書いていくことが重要です。
この演算子を使うにおいては、影響範囲はこのブロックの中です。今回の場合は44から46行目です。この中の影響範囲を十分に気にしていれば制御が効くでしょう。もし危険だと思った場合、プレコンディションを入れたり、ガードをホワイル行の前や直前に入れておくことも考えられます。これがまさしくint
型ですよ、という期待されるものかどうかチェックする。この程度で十分対応できると思います。
また、ジェネリックスを使ったファンクションにおいて、受け取る引数を何型でもいいけど符号付きの数値型に限定する、といった配慮もできます。例えば、サインドインテジャやサインドニューメリック型を使うなど。
いろいろとマニアックな領域に入ると、細かいことを気にして書くことになりますが、普通はそこまで気にしなくても大丈夫です。Swiftは、気にしなくていい部分をちゃんとフォローしてくれるので、とてもありがたい機能だと思います。
それから、オプショナル型のnil
を明示的に扱うという点も重要です。要は、値がない場合にはnil
としてしっかり扱い、それをプログラマーに対して明示的に書かせる機能がある。これもSwiftの良いところです。また、メモリ管理を自動で行ってくれる点も非常に助かります。この辺りは他の言語でも自動管理されないことがあるので、助かりますね。
コンパイラーのパフォーマンスとユーザビリティの両立ができている点も評価できます。オプショナルも同じで、暗黙的にアンラップしてくれるオプショナルなどあります。これについては、またオプショナルの話になるときに詳しく見ていきたいと思います。
エラーハンドリングによって、予期しないエラーからの復帰も簡単に行えます。昔から多くの言語が例外処理を提供していますが、Swiftはエラーを詳細にプログラマーに伝え、ランタイムにおいてリカバリを行うためのコードを書かせることが容易です。Swiftがメンターのように機能し、エラー処理の書き忘れを防いでくれる点も大きいです。
他にもif
文やswitch
文で、コンパイラーがメンターとして機能することが多いです。プログラマーに寄り添った言語というイメージですね。初学者にも非常に優しい言語です。
いい感じの時間になったので今日はここまでにします。引き続きSwiftの言語仕様をじっくり見ながら進めていこうと思います。1時間お疲れ様でした。