型が持つ意味を考える

型が持つ意味を考える

私は長いことPythonを使ってきたため、これまで真剣にプログラミング言語の型について考えたことがありませんでした。

これまでの「型」のイメージは、

というものでした。

というのも、動的型付け言語なら型をそこまで意識する必要がないため、初期化をIntで行ってもStringを代入することができます。

x = 10
x = "abc"
print(x)

静的型付け言語では、基本的にこれを禁止となっています。

初期化と代入で型が変わることで、意図しない動作を引き起こす可能性が生まれやすくなります。

例えば、コードが大規模になってきて変数への代入によって型が目まぐるしく変わるコードを書くと、その変数の型の状態を意識しなければいけなくなります。 また、分岐によって型が変わるということがある場合はさらに複雑性が高まります。

これはプログラマの頭の中で型推論のインタプリタを動かし続けていることに等しいと思います。

そのため、動的型付けの言語でもこのようなコードを基本的に書くことはありません。 また、動的型付けの言語であっても JSのconstのように再代入不可として定義することもできるため、言語レベルで安全性を担保することはできます。

しかし実際、Pythonのような動的型付け言語の場合、文字列の数字とIntの数字は意識しなくても同じ挙動になることも多いです。 そのため、ついこのようなコードが混入することも度々発生します。 一方で、型の違いによって挙動が変わることも往々にしてあり、このケースは本当に発見しにくいと思います。

では、型を明示することは、ただバグを減らすためにあるのでしょうか?

私はそれだけではなく、「型」は意味を表しているのだと感じます。

標準で提供されている型であるプリミティブな型では抽象度が高く、意味を持たせることは難しいです。

せいぜいこの変数やプロパティは整数であるとか、関数なら、戻り値は文字列であるということにとどまります。

実際は意味を持たせるというより、意味を捉えることが難しいという方が正しいかもしれません。

「型」は新しく定義することもできます。

つまりクラスやインターフェースを使って、型を定義することで、型自体に意味を持たせることができます。

型システムの真骨頂はここにあると考えらます。

例えばバックエンドAPIについて考えてみましょう。

このAPIは商品の情報を返却するエンドポイントです。

簡単に考えるため、商品はid, name, priceを持つものだとします。

プリミティブな型だけを使うなら、

ここで型を自分で定義して、Id型、Name型、Price型というものを導入してみることにします。

そして、それぞれの型で値の取りうる範囲を制限することもできます。

とした場合、それぞれの型に意味が出てきます。

プリミティブな型を使っていた場合は、プログラミング言語で定められたLong, String, Intの制約が反映されますが、 型を定義することでドメイン知識に沿った型による制約を反映させることができます。

これこそが、型が本来持つ力だと考えています。

型を定義し、設計に組み込むことは多くのメリットを生みます。

というように、システムの複雑性を減らしつつも、表現力は向上するという方向性に寄与すると思います。

最後に

今回は「型」に着目して、「型」が持つ意味とその力について考えてみました。

「型」は設計を意識しないとあまり関心が向かないところだと感じます。

また、昨今は動的型付けの言語から静的型付け言語への回帰が起こってきているかなという感覚もあり、 その背景には前回の記事でも書いたように、初速は確かに出るけど、大規模になると厳しいという感覚があるのではないかと思います。

また、ドメイン駆動開発も浸透してきていることも背景にあるかもしれません。