https://youtu.be/B5a2aFSYkwk
今日は、今まで予告しつつもなかなか辿り着かなかった 変数宣言におけるルール
のところに記されている 変数や定数の書換可能性
のところから見ていきますね。なんとなく不思議な本の構成ですけれど、そこに何か意図があるのか少しばかり気にしたりしつつも書籍の流れに沿って眺めていこうと思ってます。どうぞよろしくお願いします。
——————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #99
00:00 開始 00:46 予約語を名前に使うことについて 02:04 引数名に default は使う? 05:34 引数ラベル名での default について 10:47 予約語を名前に使うのはなるべく避ける 12:20 変数の監視に寄せた変数名 14:54 default という名前の冗長性 18:32 何の default であるかが判るかどうか 23:50 オートクロージャー 29:20 オートクロージャーの使われる場面 30:02 列挙型における default とオプショナル表現 35:09 なぜかここで「値の変更」の話 35:56 互換性のある型 38:19 クロージング ———————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #99
はい、じゃあ今日は「The Basics」の「ネーミング、コンスタントアンドバリアブルズ」を引き続き見ていきます。その前に、前回の話で補足が必要な点がありましたので、もう少し詳しく見ていこうと思います。
前回、変数や定数で予約語と同じ名前は使えるけれども、可能な限り使わないようにしましょう、という話をしました。たとえば、以下のようなコードを書いて予約語を使うことは避けましょうという話です。
let `default` = "some value"
予約語は一般的な意味を持つため、変数名や関数名として使うのは意味が広がりすぎてしまい、混乱を招く可能性があります。
具体例として「default」という予約語の使い方について触れました。このキーワードは、例えばスイッチ文のデフォルトケースなどに使われます。以下のように書くことが多いです。
switch someValue {
case 1:
print("one")
default:
print("other")
}
この「default」という言葉は汎用的なので、変数名や定数名として使いたくなるかもしれません。しかし、公式のAPIでもこのように予約語が使われることがあるため、一概に避ける必要がない場合もあるかもしれません。
具体例として、ユーザーデフォルトに関するAPIを挙げましたが、例えば以下のように使われます。
UserDefaults.standard.register(defaults: ["userSetting": true])
こういった場合に、「default」という名前が公式のAPIで使われていることが見られます。とはいえ、選択の余地がある場合は、やはり予約語を避けるべきです。他の名前にしておけば、混乱を避けやすくなります。
また、他の予約語の例として「in」を挙げます。以下のようなコードで「in」を使うことがありますが、これも予約語です。
for value in collection {
print(value)
}
この場合、「in」という名前を別の変数や定数で使わないようにする必要があります。内部名では区別がつかないため、こうした予約語を避ける策が用意されています。
たとえば、以下のように内部変数名を変更することで予約語を避けることができます。
func someFunction(parameter: Int) {
let internalParameter = parameter
}
これは、APIデザインガイドラインや、英語として意味が通る名前を選ぶという観点からも重要です。予約語を避けることは、読みやすく保守しやすいコードを書くための一つの手段です。次回も引き続き、このような話題について見ていきたいと思います。 7行目に戻りますけれど、さっきのサブスクリプトの例を対応したような関数です。けれど、こっちはデフォルト。そうですね。この後、内部変数名を用意しなかったとすると、書きにくくなるからやめようね、くらいになりますかね。こういうふうな感じでバックティックいれないといけない。それが嫌な場合は、嫌な場合はというか、そうなってしまうからデフォルトバリューを選びましょうね、みたいな話になってるんですかね。
これはね、前回、このスライドを話したときにも言いましたけれど、絶対に選択の余地がない場合を除いて、みたいな少し強めな表現にはなっていましたけど、結局のところは、なるべくキーワードは使わないようにしていこうね、という雰囲気程度で捉えるのがいいんでしょうね、ここはきっとね。多分ね。あくまで自分の主観で言ってますけどね。
そんなふわっとした中での書ける理由としては、何か書くのが面倒だなあ、もそうだし、名前が強調されるなあ、もそうだし、もうちょっとがっつりとAPIデザインガイドラインに即していないから、みたいな理由もそうだし、とにかくどんな理由であれ、ちょっと強調じゃないかなと思って避けてもいいと思うんですよね。さっきのサブスクリプトの例では丁寧にデフォルトバリューになってましたけど、でも避けるとコードがバックティック入ってきちゃうから、バックティックを避けたい意味でこうなってくるのか。
他に別のいい表現があればまた別ですけれどね。デフォルトだからって工夫して変な名前つけても分かりにくいから、デフォルトバリューな気もしますね。このノリでなんとなく思い浮かぶのが、バリューがあって、これのディズセットは名前をつけたいなあ、っていうときに oldValue
にするでしょうし、名前つけなくていいなら、バリューの場合はね、もっと別の名前のときには例えば name
とかだったとしたときには oldName
って名前をつけたくなる、みたいな、そんな雰囲気ですかね。
このデフォルトバリューね。そんなノリでここ oldName
は妥当ですね。これデフォルトっていうノリだとこうなりますよね。これも人の感覚によるのか、なんとなく「古い何なの」って思っちゃうような気がして、oldName
のほうがなんとなく自然な気がするみたいな風に思ってくると、デフォルトって何なのってなって、デフォルトバリューってなってきて自然なのか。
そこまで考えてくるとそんな気がする。やっぱりデフォルトバリューってつけたほうがいいのか。でも、デフォルトで通じる気がしてたけど、ちょっと不足そうに感じないかな。どうなんだろう。まあいいや、なんとなく自分はそう感じたのでデフォルトバリューって今後つけたいなと思いました。
上調に見えますかね。どうなんだろう。最初自分は上調に見えてたんですけど、デフォルトバリューが。でも上調じゃないね。ラベルのときにはデフォルトバリューまでくると上調に見えないように感じる。なかなか面白いですね、この感覚の差はね。
オルト、そうね、分かるからなのかもね。バリューが何で、デフォルトは何ですよっていうね。あと、バリューという言葉が汎用的すぎるっていうところもあって、さっきのサブストリート、これがキーとして何らかのキーを取って、それでデフォルトバリューとして、デフォルトとしてか。また言いたいことが出てきたぞ。デフォルトバリューとしてね、例えばバリューが int
で取れるとして、こういう感じにしましょうか。
こういうふうな定義があったとして、このときに最初言おうとしていたことは、デフォルトバリューって書くと上調になるっていうお話をしようかと思ったわけですよ。これが上調になるのはどういう場面かというと、例えば配列とかですね。配列の append
メソッド、ミューティングになっていますけど、ミューティングファンとあとラベル省略してエレメントで、エレメント型を取るよみたいな、こういう append
メソッドありますけど、ここでエレメントっていうラベルは略さない。
略すっていうのが一般的で、略さないのは、このエレメントっていうのが上調になりすぎて API デザインガイドラインの中に一字一句、過不足なく表現する中でこのエレメントが浮いちゃうっていう。例えば names.append
とか言って、名前を入れようっていうときに、names
っていうレシーバーに対して append(Tarou)
でいいところを、それを appendElement(Tarou)
って書いてしまうと、エレメントっていう言葉がなくても済むのに入ってるっていう捉え方もあるし。
あと、ここで書くとしたら name
ですよね。 書くとしたら、いや、書かないでしょって思いそうな気もしますけど、あえて書くとしたら、ネームズのエレメントっていう発想はどちらかというとコレクションのエレメントっていう発想になってしまって、あんまりネームズをちゃんと見ていないわけです。それがちゃんとネームズという主体だったら、名前って分かるでしょって省略するのがガイドラインですが、入れるとしたらネームズなわけです。それが少し汎用的になってきて、ネームズが浮いちゃう感じがします。このような理由で、汎用的な部分はなるべく省略したいという感覚でデフォルトバリューのバリューも省略しているのかなと最初は思いました。
でも、よくよく考えると、このデフォルトは何のデフォルトなのって思う可能性はなくないですか? このデフォルトがキーだったらどうしよう、って省略できないからオプショナルになっていたら迷う、っていう程度ですかね。このときはnil
を入れればデフォルトのキーになるだろうとか、いろいろ想像するとデフォルトは普通バリューだよねって思うけれど、そこまで考えさせないですよね。デフォルトバリューってなっていたら、どっちがいいと思いますか? デフォルト。あんまりデフォルトで書いてそこでキーが入ることは、あんまり思わないかな、想像的に。
確かにそうですね。ただし、まあただしっていうのも大げさなんですが、デフォルト。他でこういう画面があったでしょうか? 何だっけな、曖昧になっちゃうからちゃんと名前を付けようねっていうような場合、何かあったかな、忘れちゃったな。まあいいか。引っ掛けちゃったかもしれないんですけど、デフォルトバリューにするなら後ろのバリューはいらないですよね。そうですね、いらないですね。
これは、何でバリューだといけないんでしたっけ? バリューだとデフォルトではなくバリュー。これはデフォルトだったときに置き換えるアサインという意味でAPIを作っているという意図があるので、代入ではないですよね。例えばデータストアからあるキーを(IDとか何でもいいやパスワードにするかな)取得するんだけど、パスワードが設定されていなかった場合には定義としてこれを使うよっていうね、こういった書き方を目指しているので、このときにバリューだとちょっと意味が分かりにくいですよね。
なのでデフォルトというアイデアなわけですが、これデフォルトバリューの方が分かりにくくないってどうだろうっていう、そういった感じのお話です。バリューはとりあえず広くなりすぎるからOKだなと今は思いましたけど、そのノリでデフォルトは広くないですかどうですかっていうところで、さっき聞かせてもらった通り分かるんですよ。だからそれで落としどころとして13行目でOK、18行目でOKと捉えるのが多分主流だと思うんですよね。
こうしゃべっているとだんだんデフォルトでいい気がするなってまた思います。でも、19行目みたいな何のデフォルトなのかが分からない、ここが大事なのかな。要はね、今回の19行目で見るなら何のデフォルトかまでは分からなくはそうそうならないよね、って多くの人が確かに納得できそうです。これがもうちょっと変なところで突然デフォルトとか出てくると、何のデフォルトなんだろうっていう懸念が出てくる場合にはデフォルトバリューって付ければ十分なのかもしれないですね。なんとなく自分の中でも境目が見えてきたような気がします。際どいところなんでしょうね、状況に応じてね。
で、サブスクリプトの話なのでこれでいいんだな、さっきのね、標準ライブラリーのコード。コメントもらったやつ。じゃあ大体オッケーかこれでね。特にこの中で気になること他にありますかね? 大丈夫かな。まあ一応落ち着いた気がします。
はい、じゃあ、とりあえず次行っちゃおうかな。また何かあれば、思い出して考えればいいでしょう。そうですね、コメントでもいただいてるけれど、デフォルトバリューがオートクロージャーになっているのはとても上手ですね。うっかりすると何も考えずにサブスクリプトじゃなくて別のにするかな。えっと、さっき算数にしよう・・・えーと、ここの? 何か書き換えちゃったっけ。あ、ここにありましたね。バリューズ、コメントアウト? あーあったあった、ここだ、calculate
。間違ってた。綴りはcalculate
ですね。で、デフォルトバリュー、ここオートクロージャー確かに上手ですね。こうしてあげないと評価しちゃうんですよね、先にね、デフォルトバリューを挿したときに。 オートクロージャーにしておくことによって、デフォルト値が必要な状況になった際に初めて評価されるようにできます。これにより、例えば計算を行う関数に値を渡す際、通常は問題ありませんが、戻り値を返す関数をデフォルト値として渡す場合に問題が発生します。
オートクロージャーを使わない場合、デフォルト値を使う必要がなくても関数が実行され、その結果が返されます。しかし、オートクロージャーを使うことで、関数の結果(アクション)はデフォルト値が必要とされるまで評価されず、そのまま渡されます。これにより、重い計算を含むデフォルト値を渡す場合でも、必要がなければその計算を無視できます。
ただし、注意点として、評価式が実行されない状況により結果が変わるような場合には、オートクロージャーが意図した通りに実行されない可能性があります。例えば、アクションが実行されるたびに状況が変わる場合、実行されたか否かで結果が変わる場合などで注意が必要です。
イテレーター(イテレーターのネクスト)が特に重要になります。例えば、23行目
でイテレーターのネクストがどのような状態になるかによって重要性が変わります。今回はコンパイルエラーが発生する部分もありましたが、オプショナルなイテレーターに対してどう処理するかを考える必要があります。
オートクロージャーを使う際の基本的な利点として、不要な計算を避けることができる点がありますが、その結果を保証するためには状況に注意する必要があります。
余談ですが、論理演算子 ||
(または)もオートクロージャーとして機能する場合があります。左側の条件が成立したら右側を評価せずに次に進むという点で、オートクロージャーの一種と言えます。
また、前回話したデフォルトのキーワードについても触れますが、特定の場合にデフォルトという表現を使うのは控えた方が良いこともあります。特に標準的な値を使用する場合など、他の表現(例えば スタンダード
や シェアド
)が適していることもあります。
ディフォルトのキーワードを使う場合は、より明確にするために具体的なタイプ名を付けることが推奨されます。例えば、タイプAやタイプBといった具体的な名前を使うことで、ディフォルトという表現が曖昧にならないようにすることが大切です。
以上が今回のオートクロージャーやデフォルト値の話でした。これにより、より効果的にSwiftの機能を利用できるようになると思います。 オブジェクトとしてデフォルトの型を持たせたいが、パラメータに渡すときにはデフォルトではなく特定のものを指定したい場合、表現が非常に曖昧になることがあります。このような場合には、デフォルト表現を外して特定のものを指定することが必要です。
例えば、オブジェクトではデフォルトの型を持たせ、アクションのときにデフォルトにしたい、といった状況も考えられます。どちらのケースでも、オプショナル型を活用することで、デフォルト的な表現を作れます。スイッチ文も途中から改善されて、ケースがcase typeA:
とcase typeB:
のように書けるようになっています。これにより、最初からデフォルトのケースをあらかじめ埋め込んでおかなくても、オプショナルを使えば対等に書けるし、オプショナルではないものを除外することも可能です。特定のケースしか取らないということも表現できるため、オプショナル内でデフォルトを入れるというのはあまり必要ないかなと思います。
前回のおさらいが少し長くなってしまいましたが、次に進みましょう。今回の話題は名前についてです。しかし、スライドを見ると、なぜかアサインの書き換えの話になっていますね。前に「アサインは書き換えられるよ、書き換えられないよ」といった話をしましたが、それほど昔のことではありません。この表で出てきた内容が、なぜここでまた出てきたのでしょうか。
既存の変数の値を互換性のある型の別の値に変更可能という話でした。具体的には、例えば元々あるクラスの値を、そのクラスのサブクラスの値に代入するようなケースですね。イント型にはストリング型は入れられませんが、宣言した型と互換性のあるもの、サブタイピングでも構いません、とにかく互換性のあるものしか入れられないという話です。Any
型なら全ての型がサブタイプとなるので、そのまま入れることができます。これはlet
とvar
の話だと思います。
特に新しい話ではなく、変数と定数の違いについて再度確認しただけですが、変数は何回でも値を変更できますが、定数は一度しか値を設定できませんということですね。もし追加で何か思いついたことがあれば、勉強会や他のチャンネルでシェアしていただけると嬉しいです。
それでは、今日はここまでで勉強会を終わりにしましょう。お疲れ様でした、ありがとうございました。