https://youtu.be/h7SA5GAC2q8
前回から The Basics の オプショナル
の節に入って、その存在意義的なところを眺め始めましたけれど、今回もそんな辺りを引き続きみていきます。内容的には オプショナル
の概念的な特徴の続きと、その基礎的な構造などの根本的なところを見ていけたらいいなと思っています。どうぞよろしくお願いしますね。
——————————————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #137
00:00 開始 00:26 前回に紹介できなかった NSNotFound と SwiftUI 周りの話 01:11 NSNotFound はなぜ Int.max なのか 02:36 インデックスに負の値をとる可能性 03:31 Swift もインデックスに負の値をとれる 04:02 他の言語でのインデックス文化も考慮した結果? 06:14 NSNotFound が Int.min だったとしたら? 06:53 BASIC 言語を知っている人 08:10 過去の遺産も礎として 09:07 プログラミングの入門として 10:02 アルゴリズムの学習も効果的かも 11:35 インデックスに負の値が許容されている言語 12:08 視野の広がりを感じるひととき 13:10 許容範囲の広さは大切 13:56 SwiftUI であったらしい不具合の事例 15:16 値がないなら「値がない」と表現すること 17:06 列挙型で「それ以外」を表現するとき 19:30 エラー表現での「それ以外」の例 20:44 プログラミング言語の選択肢での「それ以外」の例 21:24 オプショナルについて見直してみる 21:49 知らない概念を使っていくのは難しい 22:21 Objective-C の nil との違い 24:33 NSNotFound によるアプローチの難しさ 25:26 オプショナルにより責任が利用側から定義側に移った 26:55 型変換では変換先が責任を持つ 27:15 モジュールをよく知っているのはモジュール設計者 28:06 あらゆる型の値の不在を示せる 29:12 オプショナルは列挙型で表現するのと同じ感覚 29:52 オプショナル型自体も列挙型 30:45 Swift 言語による手厚いサポート 32:11 オプショナル型の入れ子表現 33:29 インデックスを符号なし整数で扱われる可能性 ———————————————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #137
今日はオプショナルについてお話しします。前回もオプショナルの話をしましたが、今回は概念的な特徴についてお話しする回になります。
まずはオプショナルの話に入ろうと思いますが、前回最後に自分の環境でURLを開けなかった話をしました。それは少しオプショナルの話から外れるかもしれません。
Swiftではオプショナルという表現ができるようになりました。そのため、インデックスが見つからなかったときには「nil」という表現ができます。しかし、Objective-Cの頃は「nil」という概念がなかったため、インデックスが見つからなかったときには特別な値を使用しました。具体的に言うと、これはNSNotFound
という定数です。正確には定数ではなく定数定義(define)になっていたと思います。この定数の中に「この値はありえないだろう」という値を入れていましたね。
でも、なぜこれがInt.max
だったんでしょう。マイナス1ではダメだったのでしょうか。ちょっと話がそれましたがどうですか。オプショナルという表現がなかったときに、インデックスが見つからなかった場合、通常の配列のインデックスはObjective-Cの文化では0から始まるため、0は使えません。見つからなかった場合に0を当てるわけにはいかないので、マイナス1とかを使いたくなりませんか?
実はそういう言語も多いですよね。インデックスが見つからなかったときにマイナス1を返す言語。具体的に何言語かは忘れましたが、言語というより標準ライブラリの問題かもしれません。それではなぜInt.max
を使ったのかがわかる人はいますか。「これだから都合がいいんだ」と説明できる人がいれば教えてください。
普段、アクセスできないような値を使うことでわかりやすくしているのかもしれません。もしC言語やC++での使用を考慮すると、確かにマイナス1という選択肢もありますね。しかし、メモリが壊れたりするリスクがあります。保護機能で弾かれる場合もありますが、確かにマイナス1は危険です。そう考えると、Int.max
を使うのは安全かもしれません。
言語によってはマイナスのインデックスもありますね。Cの場合、ポインタを使うときにマイナスのインデックスを指定することで1つ前にアクセスすることができます。ただし、Objective-Cの文化においても同様のリスクがあります。ポインタが指しているアドレスが先頭アドレスとは限らない場合が多いので。
教えてもらったリンクに何が書いてあるのか表示しようとしていますが、正直、イントマックスを使う理由を考えると、C言語などの配列の範囲を考えて安全性を万全にしたかったのかもしれませんね。
Swiftにおいてオプショナルは、インデックスが見つからなかった場合にnil
となるので、こうした悩みから解放されると言えるでしょう。この点で、オプショナルという概念は非常に便利ですね。 基本的に、プログラミングの入門言語としてはBASICを使う人が非常に多いと思います。私もMSX-BASICなどを使っていました。しかし、今の時代ではBASICは入門には適していないように感じます。
C言語などの方が良く、ビット演算なども強制的に行うことになるため、アルゴリズムの理解が重要になります。ただ、大学の授業では深く踏み込んで学ばない傾向があります。特に、最近はアルゴリズムを学ばなくても済むことが多くなってきました。それでも、アルゴリズムを知ることで特定の問題に対して最適な解法を見つけられる応用力が身につきます。
話を元に戻すと、Swiftではオペレーターやサブスクリプトを自由に使えるため、要素の値を取ることが一般的です。これは例えば、Smalltalkではゼロしか使えないといった制限があります。NSNotFoundが-1ではなく、INT_MAXになるのも不自然ではないと感じるようになり、これによって自分の視野が広がったと感じます。このような柔軟性を持つことが大事だと思います。
プログラムでは徐々にこのような許容範囲が広がってきましたが、一般的な事柄ではまだ思い込みが先行することが多いです。その許容範囲を広げていくことが重要です。このような違いを受け入れることで、個人的には非常に良い経験になりました。
最後に、NSNotFoundの話に関しては、具体例を紹介してもらいましたが、時間がなくて全部理解しきれなかった部分もありました。ただ、その中でわかった例も紹介しようと思っていましたが、結論としては全てを理解するには至りませんでした。 とりあえず、NSNotFound
が関わって重大なバグがあった、というお話になります。Xcode 14でも同じバグが発生するらしく、具体的に説明すると、最初のカラーデータのセクション2000を使用するとクラッシュしますが、他のカラーデータのセクションでは問題が起こらない、というケースのようです。その問題に NSNotFound
が絡んでいたという話です。
NSNotFound
のエラーティファクタがこの問題に影響していたかははっきりしませんが、大事な点は、値の表現範囲内で特別な値を表現することによって、想定外の状況が発生しやすくなるということです。NSNotFound
といった発想は昔は一般的でしたが、現在では nil
という別の次元の値の表現方法が存在しています。これを使うことで、実際の値と特別な「無い」という値を明確に区別できるため、安全性が高まります。この発想に馴染むことが大事です。他の言語から来た人など、nil
に馴染みがない場合は特に意識を向ける必要があります。
nil
の安全性を意識することは重要で、これを使いこなすのはなかなか難しいですが、意識を向ける必要があります。例えば、Swift や Objective-C、C、Basic などの言語では通常問題は起こりませんが、特定の状況下で問題が発生することがあります。たとえば、オプショナルな値を使うかどうかの判断や、エラーの表現方法などです。
エラーの表現としては、enum
型を使ってエラーを分類し、unexpected error
を含めるかどうかの判断なども重要です。こういったことを意識するだけでも、プログラミングの安全性が高まります。
nil
に慣れていない人は、「nil」という概念を取り扱うのが難しいかもしれませんが、慣れていけば安全且つ効率的にコードを書けるようになります。Swift のオプショナルについてはもう少し深く掘り下げていきましょう。前回の勉強会でオプショナルについて見たことがあると思いますが、T
や Objective-C には存在しない概念ですので、馴染みのない方は特に注意深く見ていくと良いと思います。 前回も話した通り、Objective-CとSwiftの違いについて注目してみましょう。このコンセプトはObjective-Cでも見られますが、オブジェクトを返すメソッドで有効なオブジェクトがない場合にnil
を返すのと非常に近いです。ただし、Swiftではこの概念が少し異なります。これが重要なポイントです。前回もポインタとの違いについて話しましたが、興味がある方は前回の内容も参考にしてみてください。
実際、Objective-Cではnil
がオブジェクトに対して機能していましたが、Swiftではオプショナルが登場し、構造体や基本型(例: Int型)でも機能するようになりました。これによって、Objective-CやJavaなどで一般的であったnil
とは異なる要素を感じ取れるようになるでしょう。これにより、オプショナルの世界が理解しやすくなります。
過去には、Objective-Cでは特殊な値であるNSNotFound
を使っていましたが、Swiftではオプショナルが導入され、これを使わなくて済むようになりました。これは非常に嬉しいポイントです。Objective-Cのアプローチでは、特定の状況でNSNotFound
を返すことが想定され、それを検証する必要がありました。
一方、Swiftのオプショナルは、APIの設計者が値が存在しない可能性を知っており、それをOptional
型の戻り値として指定します。このようにすることで、APIを使用する側が値が存在しない可能性を事前に知ることができます。これは、利用側にとって非常に大きな利点です。APIの設計者が責任を持って知らせることで、使う側が特別な値について知っているかどうかに依存しません。
これは、普通のコミュニケーションでも同様です。業務連絡などで、話し手が知っている情報を暗黙的に相手に伝えるかどうかが重要だからです。Swiftの設計では、APIの設計者がその型について詳細に知っているという前提に基づいています。型変換においても、変換先が変換元を受け取り実装するという考え方です。
また、Swiftではアクセスコントロールがモジュール単位で指定されており、例えばprivate
、internal
、public
などがあります。internal
はモジュール内でのみアクセス可能であり、モジュールを作っている人はそのモジュールの値を全て把握しているだろうと想定されています。これにより、自分自身の範囲内で情報をより確実に把握できる可能性が高まります。
総じて、Swiftでは戻り値をオプショナルにすることで特殊な値を表現できるようになっており、安全性の高い設計が可能です。Swiftのオプショナルは、NSNotFound
のような特別な定数を必要とせず、あらゆる型の値の不在を示すことができます。これに尽きます。 この辺り、何か他に補足しておくところはあったかどうか確認しましたが、大丈夫そうですね。Nilに関してスポーツ証券が割り当てられているように見えますが、これは単なる誤りです。要は、モジュールがAPIを通じて何かを返すとき、その値の表現範囲をOptional型を使って表現しているということです。
例えば、Int?
型を使えば、値がInt
であるか、あるいは値が存在しない(nil)という状態を表現することができます。これにより、数値が存在しない場合の状態も考慮した設計が可能になります。
このように、SwiftにはOptional型という特別な型があり、これは値が存在するかしないかを明示的に表現するためのものです。Optional
型は汎用的に使用されており、Int?
だけでなく、他の型でも同様に使うことができます。
Optional型を使うことによって、Swiftでは「値が存在しない」というケースを特別視せずに、安全に自然に記述できるようになっています。これにより、プログラムがより堅牢で安全なものとなります。
また、Optionalの値を取り出す方法もいくつかあります。例えば、!
を使って強制的に取り出す方法がありますが、その場合はOptionalの値が必ず存在することを保証しなければなりません。他にも、if let
や guard let
を使った安全なアンラップ方法があります。
コメントでいただいた内容で、Optionalを二重に重ねた場合の表現もあります。例えば、Int??
は「OptionalのOptional」という意味になります。こうすることで、より複雑な状態を表現することもできますが、状況に応じた適切な使用が求められます。
最後に、Objective-Cにおける NSNotFound
のような特別な値を使った方法についても話がありました。Objective-Cでは、特定の意味を持つ特別な値を使うことで、例えばインデックスが見つからない場合を表現します。しかし、Swiftではこのような場合もOptional型を使うことでより直感的に表現することができるため、エラーハンドリングがシンプルになります。
以上、本日の勉強会はこの辺で終了とします。次回は月曜日が祝日でお休みなので、次は水曜日に予定しています。では、お疲れ様でした。ありがとうございました。