https://youtu.be/Rf-ex7GBx64
今回もいちおう 定数と変数
の 命名
について眺めていくのですけれど、なぜかこのセクションの中で 変数や定数の書換可能性
について触れられています。先日に見ていった内容と似ているような感じはするものの、とりあえず本書の流れに沿ってそのままこちらを今日は見ていくことにしますね。どうぞよろしくお願いします。
————————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #96
00:00 開始 00:47 変数宣言 01:43 宣言した変数の型は変更できない 03:08 シャドーイング 10:43 シャドーイングと隠された変数 13:16 if let shorthand 14:14 if let shorthand 表記についての是非 16:54 シャドーイングが使える場所 17:40 Optional Binding として見たときの違和感 20:01 オプショナルパターンとの比較 26:41 そのうち普通に慣れそうな印象 27:47 同じことをするなら1つの書き方を取る方針 29:17 Swift に従うのもオススメ 31:05 while でも if let shorthand が使える? 33:43 最後は慣れの問題 35:46 書き換え可能な Optional Binding について 37:02 while let でループを抜ける方法 38:00 クロージング —————————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #96
では、始めましょう。今日は変数宣言におけるルールについて見ていきます。まずはネーミングについてのお話ですが、その前に少し異なる話が出てきます。まずはネーミングに沿ったお話になるのかなと思います。
では早速見ていきましょう。変数宣言についてです。一度特定の型の変数や整数といったものを宣言すると、同じ名前で再宣言したり異なる型の値を格納するために変更したりすることはできなくなる、という話です。
前回のお話の中で、自分が混乱しがちだった部分として、変数宣言を見ていくときに代入するインスタンスの方、要は左辺の話をしているのに右辺の話と取り違えて話してしまうことが結構ありました。今回もこの文章を読んでいて、うっかり取り違えそうだなと感じたので、ちょっと強調しておきます。同じ名前で再宣言したり異なる型の値を格納するために変更したりできないというのは、左辺側の話です。この表現も少し分かりにくいかもしれませんが、例えば let value: String = "A"
という宣言をしたときに、再代入はできないよ、という意味ではなく、同じ名前で再宣言したり、例えば value
に Int
型を入れたいといったような変更はできないということを指します。
これを理解するために、以前の例で同じ名前の変数を再宣言しようとしてエラーが出た経験がある方がいるかもしれません。これは自然によくやることで、特に注意する必要もなく当たり前のこととして受け入れられているかもしれませんが、改めて別の型として同じ変数名を使うのはどういった場面で行われるか、具体的に考えてみましょう。そして、その技法のことを何と呼ぶかも考えてみましょう。
例えば、関数の中で value
というパラメータを使うとします。Int
のオプショナル型を受け取る例で考えてみましょう。具体的な場面を考えると、ViewController
の中でコールバックのメソッドを書くときなどが挙げられます。デリゲートパターンで weak var
を使って自分自身をデリゲートとして持ち、関数としてコールバックを呼び出す例などです。
weak var delegate: ViewController? = self
func viewControllerDidSomething(_ controller: ViewController) {
// ここで何かを処理する
}
このような例で、変数 value
に対して異なる型の値を再宣言することができないというのがポイントです。オプショナル型や別の型として同じ変数名を使うとエラーになります。このことを理解して、実際のコードを書く際にも注意しましょう。
以上が、変数宣言におけるルールについての説明です。 このときに、オプショナルだから if
とか if-let
, guard-let
で処理する方法もありますが、もうちょっとシンプルに書きたいと考えています。例えば、コントローラーがあって、これはとてもわかりにくいのですが、要はシャドウイングという話です。
シャドウイングとは、同じ名前の変数を異なるスコープで再定義することです。オプショナルではあるけれども、実際には値があることが確実な場合に使います。例えば、controller
がオプショナルであっても、すでに確実に値があると分かっている場合、controller!
と言って強制的にアンラップする方法です。
let controller: ViewController? = ...
if let controller = controller {
// ここでは controller はオプショナルではなく、通常の型として扱えます
}
ここで、オプショナル型を通常の型に変換するための変更を行っています。これは、異なるスコープ内で同じ名前の変数を再定義することで、あるスコープ内ではオプショナルではなく扱いたいときに便利です。
例えば、次のようなコードが考えられます。
class SomeClass {
var controller: ViewController? = ViewController()
func someFunction() {
if let controller = controller {
// ここでは controller はオプショナルではなくなります
}
}
}
このように、変数を再定義することで、異なるスコープで異なる型として扱うことができます。スコープ内で定義した変数は外部スコープの同名変数を隠蔽(シャドウイング)するのです。
実際に動かしてみると…
print(x) // ここでは 10 と表示されます
if true {
let x = 7
print(x) // ここでは 7 と表示されます
}
print(x) // ここでは再び 10 と表示されます
この例では、8行目からのスコープ内で x
が再定義されており、スコープ外の x
とは別物となっています。シャドウイングによって、スコープ外の変数はそのスコープの中では見えなくなっています。
それから、Swift 5.7 から新たに導入された if-let
ショートハンドも関連するトピックです。以前、この勉強会で紹介しましたが、if-let
を使うとさらにシンプルに書けるようになります。
Kotlinでも同様に変数のスコープが変わる特性があります。例えば、check
関数の後で変数の状態が保証される場合などです。しかし、プログラミング言語の仕様をしっかり理解していないと、新しい構文ルールが複雑に感じることもあるでしょう。
つまり、同じ名前の変数を使う場合の言語仕様を理解することが重要です。たとえば、let foo = foo
と書くと、スコープが変わったことがわかります。スコープが変わることで、同名の変数が使えるようになるという言語仕様を把握しているかどうかがポイントです。
このように、プログラミング言語によるシャドウイングの特性を理解することで、より柔軟かつ強力なコードを書くことができます。以前はアンダースコアなどを付けて区別することもありましたが、シャドウイングをうまく使うことで、より洗練されたコードシンタックスを実現できますね。 そうですね、あれは良くなかったですね。ただ、それは良い変更ではあります。たとえば、self
を self
にしたり、z
を s
に変えたりしましたよね。皆さんもやっていましたけど、良い指摘です。
この3つについて、自分は15行目には賛成ですけど、18行目についてはどうなのかなと思います。でも、お話を聞くと確かに15行目も暗黙のルールですね。シャドウイングという暗黙のルールで、18行目も同様に感じます。ただ、確かに15行目とそんなに変わらない感もあります。どっちもどっちという気がしますね。
暗黙のルールを扱い始めると、15行目がきれいだと感じるようになります。ただ、非常に複雑な感じもします。
15行目のバランスは良かったと思います。新しいインスタンスを生成する感が強いので、オプショナルバインディングという言語仕様があるのですが、イコールを使った古典的な方法がまだ理解しやすいかもしれません。シャドウイングは if
以外でも使えますよね。
そうですね、guard
と if
くらいですかね。
いや、かなり使えますよ。たとえばケースでも使えます。
確かに。スコープの仕様なんですね。オプショナルバインディングはこの方法しかないので、それ自体は良くない気もしますが、書きやすくて便利なのかなと思います。多くの人にとって有効だと思います。覚えればいいだけなので良い変更です。言語仕様としてオプショナルバインディングをするわけですね。
else
に行くことが分からないかもしれません。
そうですね、let
とイコールの間に疑問符を付けることがあります。これで else
に行くケースが分かりやすくなります。個人的にはこれを正式に導入するなら、コード全体でこれを適用して、古い書き方は無くしたいですね。皆さんどう思いますか?
やはり古い方法でいいですか、それとも新しい方法に揃えたいですか。多分、新しい方法に慣れてしまえばこれが分かりやすいと思うんですが、言語仕様としてこれしか適用できない書き方を導入するかどうかの問題だと思います。
コード規約としても、この書き方を統一するのは良さそうです。リントツールで揃えたり、ルールを設けたりすることになるでしょうね。
オプショナルバインディングは else
で行くことが分かりにくいかもしれませんね。
慣れれば良いのですが、やはり同じ変数を書くのは抵抗があります。公文が微妙ですね。else
の話をさらに考えると、別の書き方もあります。たとえば、case let
という形ですね。
case let
は毎回検索しないと覚えられないですね。でも、この勉強会を続けていれば覚えると思います。
ショートハンドが使われると、クロージャの weak self
のときには guard let self = self else
となるんですかね。そうですね、セルフのことを考えれば、こうなるのかもしれませんが、まだ慣れていないので気持ち悪いですね。
ちなみに、この書き方は個人的にはやりにくいです。パターンマッチングをしているかがわかるように、?
を付けたい気がします。条件判定やパターン判定を導入することで、より意味が分かりやすくなると思います。
EM のケースでもパターンマッチングを使っているので、全然アリだと思います。 議論のせいではなかったという話ですね。こういうことなんですよね、パターンマッチングだと。ここでXX
と書かせているようなものに感じてしまいます。あれ、こっち変わるんですかね?ここは一緒ですか?いや、こっちを変えちゃったらどうにもならなくなってきますよね。要はlet
と書かないといけなかったんですよ。こうなってくると、パターンマッチング的にはこちらはアイデンティファイアパターンだったかな、バリューバインディングパターンになって、どんな値も適用されちゃうということになります。25行目は意味が違うんですよ、オプショナルとか関係なくね。という風になってくるアンバランス感がちょっと気になりますね。比べると違いが分かります。18行目と26行目は違う意味。27行目もここだけしか使えないから特殊です。
あと大事なところとしてケースが2つかどうかで、パターンマッチングになるかっていう違いがあります。if
文でも普通に使えますね。if case
、こっちですね。だからこれと18行目と19行目は違うものだという認識が広まりさえすれば、一応受け入れられるかもしれません。S?でキャストする場合は書き方にケースがあるかどうかで異なるので、やはり違和感がありますね。
今、まだないケースで実際のコードを書いてみましょうか。具体的なコード、if let as var
、こういうパターン。このパターンはもともとケース書いてるときに書けるもので、今度はif
でも書ける。このほうがケースと同じ書き方になっています。この書き方って代入いりませんでしたっけ?これが今度から要らなくなるんですかね。ケースの場合は分からないですが、要らなくなるんじゃないですかね。構文的にどうなるんですかね。同じキャストが新作チェック集団みたいな形になるけど、別の形にパターンカスタムするときはどうなるんですかね。確かにこれはオプショナルバインディングじゃなくて普通に試してみたいですね。オプショナルバインディングですよね、どうでしょう。23行目はオプショナルバインディングではなくて、パターンマッチングになっています。今も使えるパターンですね。こちらのほうが分かりやすいですね。
こういうノリと、さっきのこのノリで消したけれど、存在しない構文ですけど、このノリと確かに共通する雰囲気を醸し出していますね。22行目と24行目か。そう考えていくと、マチマチなところにマチマチな構文がもう一個増えて、まあまあ、全体的にマチマチだねというお話でいいですかね。慣れるでしょう、そのうち。そのうち覚えるもので、覚えようとなれば、大した構文じゃないですしね。意外とどうにかなるかもしれない気がしてきます。いわゆる老害的な発想です。昔は一つの書き方に統一するのが普通だったんですか?老害認定されますよ、それじゃ。使ってわかりやすいでしょとか言ったのなら、もちろん今は違いますよね。
さっきのアンダースコアの利用例。アンダースコアがさすがにヤバい人扱いされそうですけど、ここはYにして扱いたいんだみたいな。こういうときにはもちろんショートハンドが導入されても、普通に13行目みたいな書き方をするでしょうから。そうすると、いろんな書き方が存在しているわけですが、そういう場合は必要ないなら一つの書き方に統一する理由もあるはずの言語なので。もしかすると、if let x
要は16行目を書いたときに、その代入文は無駄でしょみたいな警告を出す可能性もないとは言えないですね。「こっちに直しなさいよ」みたいな警告を出すかもしれません。Twistの方向性としては、こういう警告がなかったとしても警告を出す方向に進むといいかもしれない。外部ツールに頼るのではなくて、TwistのコンパイラそのものでイコールXを排除していく。もちろん13行目みたいな書き方には警告は出さないで、Yを使っていなければ「Yを使ってないよ」みたいな話をすればいいわけです。
ちょっと良いお話だったな、自分の中で危うく老害になるのを阻止してもらえた感があります。まだ分かんないですね、まだ分かんない。受け入れるしかないって感じです。最終的には、Twistがそう言うならそうなんだろうみたいな姿勢が結構大事ですね。今までそれで結構救われてきたところがあるので、そういった姿勢を大事にしつつ、この例を今回のお話の中で出したかったわけです。簡単でいい例ですね。 それで、なるほどね。確かにこうやって if
文でシャドーイングを繰り返しやっていると、すごくまどろっこしいですけど、ショートハンドを使うといい感じに進みますね。今まではこういったものを書く時には、自分はパターンマッチングを好んで持ち出していたんですけど、これがショートハンドだとこうなるよというやつですね。そうか、慣れればきれいな感じですね。
ガードの話も出ていますよね。ガード文 (guard
) はまだ少し気持ち悪い感じがします。単語がバラバラと存在している感じがします。エルス (else
) でも使えるんだ、当たり前ですよね。エルスイフ (else if
) でも使えるんだ。フォワイル (for while
) でも使えるの? for while
どういうことだろう? var
で宣言したものとか、for while
でわざわざオプショナルバインディングを書くこともできますが、これは右辺に置くのってジェネレーターとかですよね。なので、オプション…どちらでしょうか、nil
になったら抜ける? 上は break
で抜けるのかな。これはオプショナルバインディングじゃないですかね。
だからたとえば、A
がヒント型のオプショナルであったときに for while
、これを省略しているんですよね。そうすると、一度入ったら繰り返し続けることになりますね。A = nil
もできないですよね。できないですね。= nil
は var
にしてもダメですよ。50行目の var
だとどうでしょうか。53行目の中で for while
の中では。これは難しいですね。ネット上の A
が下で、for while
の中だと50行目の A
にアクセスできないから、var
にしてもそうなりますよね。これもショートハンドだと難しそうですね。
という話をしようとしたのですが、ショートハンドでなくても混乱しているため、これはサポートをした場合、今までのやつも普通にそうなりますよ。面白いな。普段あまり for while
は使わないですからね。プレイグラウンドが動かないけど、無限ループで var
でも let
でも、A
に nil
を入れたとしても、コメントでいただいている通り、慣れの問題ですね。
リリースされて1ヶ月もすれば、「便利、便利」とみんな言ってそうな気がします。そういえば、ガード文のそもそも else
自体も最初は慣れないときに微妙と感じる人が結構いましたよね。これは大事な点ですね。言語仕様としての割り切りもありますが、ないとどっちがどっちだっけという感じになりますね。自分も最初は少し抵抗がありましたが、やはりこの辺りが必要なんだという意見も聞いたことがあります。今はそんなに話題にならないんじゃないかと思いますね。
まぁ、今回はここまでにして、次回また補足したいところがあれば話しますね。これは今回のセクションは Future Direction ですかね。if inout, foo = nil
とか、こんなことも今後やろうとしているのか。これができたら、さっきの for while
の内側で nil
を入れて抜けることができるのかもしれませんね。まぁ、これは今のところすごく好意的に見える機能ですね。
シャドウイングを生かしつつ nil
代入をしつつ、これがKotlinの話の中で nil
じゃない変数として扱えるけど nil
を代入することもできるという雰囲気に似たようなことができるようになりますね。なかなか難しいですが、自分は好印象です。人によるでしょうね。
あ、あとコメントで for while let
の話をいただきました。ちょっと持ってきますね。for while var abc: String?
とかで何かあって、それで関数 clear()
を呼び出す。面白いですね、この発想。abc = nil
、面白いですね。やるかどうかは別として、でも型とかを定義していて、プロパティを扱っているときには確かにこういう感じになりますね。普通にね。なるほど、これで抜けられるのか。良いですね。
では、いい時間になったので今日はこれぐらいにしておきますかね。ショートハンドの話、面白かったです。老害にならずに済みそうですね。実際に書いて試してみたら、すぐに慣れそうな気がしますね。わかりませんが、慣れない人がいてもそれはそれで良いと思います。では、今日の勉強会はこれで終わりにします。お疲れさまでした。ありがとうございました。