ここ数回に渡って、お気に入りな技術ブログ「その Swift コード、こう書き換えてみないか」の中から 名前空間
と enum
まわりを見ていってますけれど、みんなとお話しする中で教えてもらった情報などを眺めてみるに、これ以上ここに足を踏み入れるのは危険なのでは?みたいな不穏な感じが漂ってきました。そんな感じで雲行き怪しいこの頃ですけれど、今回はそんな雲行きまわりの話をしつつ、余談しつつな小休止の回にしてみますね。そしてまた次回以降はブログの続きを見て行けたらいいなと思ってます。よろしくお願いしますね。
——————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #266
00:00 開始 00:33 列挙型による名前空間とその所感 02:16 列挙型で名前空間を作る意味 03:33 意見の分かれるところらしい 04:54 プロパティーによる名前空間 06:32 現状の仕様で見たときにどれが最適か 10:08 名前空間と型情報 13:25 型パラメーターを活かした名前空間 14:56 型パラメーターを持つ名前空間 16:46 別名をつけた型と元の型とを区別させたい 19:37 ジェネリックな名前空間 20:37 折り合いのつかない話題の様子 22:02 名前空間キーワードに関する議論 25:00 名前空間とモジュールの違いも不明瞭 27:11 ハッキング感を拭い去りたい 28:29 名前空間とは異なる概念? 29:37 型エイリアスでは期待する区別ができない 30:58 信念を持ちつつ思うようにやってみたら良いのでしょう 32:27 クロージング ———————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #266
引き続き、このお気に入りのブログについて、Swiftの方法を書き換えてみましょうという話題です。このブログは香良屋さんの記事を参照していますね。個人的に好きな部分ですが、「名前空間にはいい名前を使う」という点が前々回から話題に上がっていて、非常に重要です。
名前空間があるというのは、Swiftが初めて登場した際の大きなアピールポイントです。名前空間と型は密接に関連しており、単に名前をつけるだけでなく、責任を伴うものです。こうした名前空間は個人的にはとても好印象です。
名前空間だけが欲しいときには列挙型(enum
)が便利です。列挙型を使えば、インスタンスを作ることができず、名前空間としての役割をしっかり担うことができます。これは当初発見された際に非常に話題になりました。
enum
で名前空間を構成する利点として、インスタンスを作ることができないため、純粋な名前空間として使える点が挙げられます。例えば、struct
やclass
でも同様のことが可能ですが、デフォルトでインターナルなイニシャライザーが自動で用意されてしまうため、意図しないインスタンス生成が可能となります。これを防ぐためにプライベートなイニシャライザーに変更することもできますが、完全に防ぐことは難しいです。
一方で、enum
はそのような問題がありません。意図しないインスタンスが存在しないため、名前空間としての純粋な意味で安心して使えます。
中にはこのアプローチに対して賛否が分かれる部分もあります。私自身もかつては否定派でしたが、解釈を変えて見れば悪くないと感じています。
もう一度ブログの内容を見てみましょう。とても短い部分ですが重要です。例えば、
enum SomeNamespace {
// ここに名前空間のメンバを定義する
}
こうした事例もあります。
また、他のブログでも同様の意見が見られます。それぞれのアプローチがあるので、自分に合った方法を見つけていきましょう。
enum
とstruct
、特にstatic
プロパティを持つstruct
での実現方法には、それぞれメリットとデメリットがありますが、純粋に名前空間として使うにはenum
が適しているかもしれません。
こうした議論を基に今一度、名前空間の役割を見直し、必要に応じて最適な手法を選択することが重要ですね。現実的な使用例を考慮しながら、自分のプロジェクトに最適なアプローチを探してみてください。 例えば、スタティックプロパティやネームスペースがどこかに存在してしまうと、インスタンスが使われている可能性があり、意味が変わってくるので注意が必要です。スタティックプロパティがあるかないかで、全体の意味合いが変わってきます。インスタンスを想定しているのかどうかがわからない状態では、コードの意図が不明確になってしまいます。
例えば、enum
をネームスペースのように使用し、ケースベースにして、スタティックプロパティをその中に所属させることで、イニシャライザーが勝手に生成されないようにすることができます。これはクラスでも同様で、デフォルトのイニシャライザーが勝手に生成されてしまうため、プライベートイニシャライザーにする必要がある場合があります。
重要なポイントとして、Swiftの名前空間は型と密接に関係しています。逆に言うと、型がネームスペースを作るという発想です。これを活かすと、struct
を使って名前空間を作り、不要なイニシャライザーを排除することができます。自然にこのような理屈が通るようになってくるのです。
多く驚かれるAPIの設計について話すと、例えば、メモリーレイアウトが公式で採用されている部分があります。これは、メモリーレイアウト関連の機能が名前空間に所属する形でスタティックメンバーとして定義されていることが多いです。こうすることで、不要なインスタンス生成を避けるための工夫がなされています。
例えば、メモリーレイアウトを受けるメソッドを考えると、struct
内でスタティックメンバーを使い、メモリーレイアウトのサイズやアライメントを取得することができます。以下のようなコードがその例です。
struct MemoryLayoutUtil {
static func action() {
let size = MemoryLayout<Int>.size
let alignment = MemoryLayout<Int>.alignment
// その他の処理
}
}
このように、使い方次第で応用力がある設計が可能です。また、メタルタイプを使うことでさらなる柔軟性も得られます。非常に重要なことは、タイプセーフティやメモリ効率を考慮して設計することです。
Swiftの設計哲学に基づいて、スタティックメンバーや名前空間を用いることで、より安全で効率的なコードを書くことができます。 他の情報があると、こういうことができるんですね。確かにこれは面白いですね。それと、メモリーレイアウトについても話をしましたが、隠蔽することで興味深い結果が得られることもあります。
名前空間の概念について、ちょっと考え方が変わってきた気がします。名前空間というよりも、それを超えた新たな「スコープ」みたいなものができた感じですね。それはクラスや他の構造体でも扱えるように作られているのかもしれません。それにより、自分自身の価値観が少し変わってきたように感じます。
また、静的なメソッドで利用されて、コンパイルタイムに扱われる部分もあります。ジェネリック型(Generic
)と組み合わせることで非常に強力になりますし、その使い方もとてもかっこいいですね。名前空間として利用しながらジェネリックなパラメータを持ちつつ、高度な操作が可能です。
型をもっと明確に区別したいときの方法として、例えばプロトコルやエクステンション、コンテナを使うなどがあります。そうすることで、特定の型だけを他の型と区別して扱うことができます。具体的な例として、Int
型を別の名前空間で使いたいが、ジェネリックな方法で扱いたい、というシナリオがあります。それに対してプロトコルを利用することで、型に名前をつけることができるようにする方法も考えられます。
ただ、名前空間だけで全てをカバーするのは難しいかもしれません。コンテナを利用することで柔軟に型を管理する方法もあります。しかし、それでも型に名前をつけるだけでは区別できない部分も出てくるでしょう。そのため、最終的には名前空間やジェネリックの組み合わせで解決するのが一つの方法となります。
要するに、ジェネリックと組み合わせて名前空間に役割を持たせることで、非常に強力なツールになります。Swiftの言語仕様をうまく活用することで、もっと多くのことが可能になると感じました。 Swiftの言語仕様に関する議論が中村さんの発言を通して展開されている内容について、話を整えつつまとめます。
まず、新しい存在感が出るとの意見がありました。これは、何かしらの新しい機能やコンセプトが追加されたことで、それまでとは異なる面白さや興味を引く要素が生まれることを意味します。しかし、ケースレス(case-less)な劣況型(enum)を使うと奇妙に感じるという意見もありました。劣況型は通常、特定のケースを扱うために使用されるもので、その点について疑問を抱いているようです。
そこで、「劣況型を敵メンバーをチェックするために使うのはありなのか?」といった折り合いの付け方が話題になりました。また、名前空間が欲しいという意見や、enum
を使うのがハッキング的であるという観点から議論が盛り上がりやすくなります。enum
でできることをわざわざ新しいキーワードを追加して実現するのはどうなのか、という意見も出ました。
具体的には、以下のような内容が議論されています。
enum
を名前空間として使うのは目的に合わない。- Swiftは型名前空間を作るため、劣況型を使うのの何が問題なのか。
- 劣況型を名前空間として使うことには問題はないが、ドキュメント化されていないためトリッキーであり、将来変更される可能性があるのでおすすめできない。
- プロトコルを名前空間として使うアイデアも議論の対象となり、混沌とした状態になる。
最終的に、名前空間を作成するには行動体(行動態)を定義したいという意見や、逆にenum
が名前空間を作成するなら、その中でプロトコルを宣言できてもいいかもしれないという広まりがあります。
Swiftではサブモジュールの概念が現在ないため、名前空間を細かく切り分けることができない現状があります。Appleだけが特定してサブモジュールを使っていますが、例えばFoundation.c
のような形です。
このサブモジュールの議論を進めないことには、名前空間の議論が進まないという形で議論が終結しています。結果として、2018年頃にサブモジュールの議論がなされて、それ以降も同様の議論が繰り返されています。最終的には「enum
を名前空間として使うのはハッキング的である」という認識が現在も続いているということです。
このようにハッキング的な解決方法に対する違和感が一つのポイントとして議論が続いています。 あれこれ改善したいところですね。先に言っておくと、おすすめだと思いますよ。実際、複雑な原稿を書くことになるから、明日ぐらいにやるのがいいかなという感じがします。でも、原稿が複雑になっていないせいか、私自身が使ってみて、すごく簡単に書けたと感じました。誰がいつその話を聞いているかとかを考えているんでしょうね。
ネームスペースとしての強さもシンプルなほうなので、そこまで混乱しないかもしれないです。混乱するよりはマシですよね。その驚きをどう処理するかですが、今話していた中で、このジェネリックのパワフルさに気づいてしまって、結論が揺らいでしまっています。意外と悪くないじゃんという感が強くなったんですが、どうしましょう。型に所属しているときに考えますよね。
とりあえず、所属されたときに列挙型が市民権を得たり、ケースレスタイプの列挙型を肯定すればいいということですよね。そういうのを使うと怒られないようにすればいいのかなとも感じます。とにかく、まずはお墨付きが必要なのかなという雰囲気を感じます。
さて、enum
の中にtypealias
を使うことができるのではないかという話がありました。実際、typealias
は可能ですね。ただ、ジェネリックな型について考えたとき、静的解析が難しくなることがありました。typealias
はその点では大丈夫です。でも、ネームスペースの話になるとまた別の話になりますね。
結論がポンと出ないまま終わっている感じですが、少なくともジェネリックな単純な名前交換ではないという発見はできたのが良かったです。ジェネリックな型でメタ情報のように思えるものがあるのは収穫でした。賛成派にも語りかけた感じがします。
本当に3ヶ月後の仕事という感じですね。ゆるく取り組んでみるというのが良い気がしてきます。みんなそれぞれの主張を持ちつつ、今はそれぞれが思う良い方法をやっていこうという段階かなと思いますね。未来はわかりませんからね。
今回はこれで終わりにします。また次回、ネームスペースやシングルトン、ラッパー、ケースレスなenum
について詳しく見ていきましょう。今日はお疲れ様でした。