“コーディングを支える技術”を読んで

コーディングを支える技術top画像

半年ほど前にコーディングを支える技術を読みました。

私が読んだのは2013年に発行された本なので、最近の技術(2022年現在)とは相違がある部分がありました。しかし、この本の大筋は業務にも役に立つものがほとんどでしたのでおすすめできる技術書です。

なぜこの本を読もうと思ったのか

私は2021年から新卒でwebエンジニアとして働いています。1年ほど仕事をしている中で、自分で技術選定をする際には候補に上がらないような古めの技術を触る機会が何度かありました。

その古めの技術を触っていく中で分かったことは根本の技術に変化はないということです。

逆に考えると新しい技術を深く理解するには元になっている根本の技術の理解が必須であることがわかってきました。

そして、その「根本の技術」は新しい技術の使い方を理解しているだけでは本質的な技術の習得はできないと考えています。

そこで読もうと思ったのが“コーディングを支える技術”です。

全体を読んだ上での感想

この本を読んだ最初の感想は「いま読んでおいて正解だった」でした。

というのも、今まで触れる機会がなかった、今のプログラミング言語ができるまでの歴史が書かれていたからです。

この本ではA技術の問題点を解決するための手段としてB技術が生まれて、B技術の問題点を補う形でC技術が生まれたという時系列で解説されています。

時系列形式で技術の成り立ちを理解していくことで、普段私たちが使っている技術がどのような歴史を辿って、どういう意図で作られた技術なのかを理解することができます。

この知識を駆け出しエンジニアである状態で身につけることができたことで、今後新しい技術が台頭してきた場合にも、その価値を判断できたり、既存の技術との変更の意図に気づくことができます。

それによって、技術のキャッチアップスピードが以前の自分よりも格段に上がったという感覚があります。

本の内容

実際私が読んで心に残っている部分を少し紹介したいと思います。

言語開発者の意図を理解する

この本のメインテーマに特定の言語に依存しない「普遍的な知識」を身につけるというものがあります。

ある言語を学んでいると特定の機能が理解できないことがあると思います。この本ではそれを理解するために、言語開発者が何を思ってその機能を作ったのか理解することが必要であると述べています。

確かにgetterやsetterなどのアクセス修飾子を機能として理解しているより、その機能がなかった時とできてからでは開発体験がどのように変わったのかを理解しておくことで、いつどんな時にその技術が使えるのか自分で判断できるようになります。

普遍的な知識を身につけるためには、言語開発者が何を解決したくてその技術を生み出したのか理解することが大切であると言えます。

ループ処理にwhile文とfor文がある理由

誰もがプログラミングを始めた時になぜループ処理にwhile文とfor文の2つがあるのだろうと考えたことがあるかと思います。

私もそのうちの一人で、この本で初めてその理由を知ることができました。

理由は「while文よりfor文の方が可読性が向上するからです

元々while文というループ文が存在していましたが、while文の中での処理が多くなればなるほどループの意図を理解するのが大変です。

その点for文を使えば最初に条件が全て記述してあります。

以下のループ処理の例だと、iを1ずつインクリメントして、3回ループしたら抜けるという条件を持っていることが一瞬でわかります。

for(let i=0; i < 3; i++) {
  //ループしたい処理
}

可読性と再利用性

関数処理

今ではどの言語でも基本的に利用できる関数にも今の姿になるまでに様々な歴史がありました。

関数は可読性と再利用性をを向上させる目的で取り入れられた技術ですが、それまではgoto文を用いて共通処理をまとめていました。

goto文は、処理を特定の行にまでジャンプさせるという意味を持っている機能ですが、このgoto文には問題がありました。それは、ジャンプした先からジャンプ元へ戻ることができないという問題です。

それを解決したのが関数です。

今では当たり前ですが、関数を呼び出した場所では関数の処理を実行したのち次の行が実行されていきます。

let a = 0;
// 別の場所に記述されている関数を呼びだす
hoge(a)
// hoge関数が正常に実行された場合は、戻って次の行が実行される
a += 1;

最初は戻る先を記録する専用のメモリを用いることで実現されていましたが、関数Xに呼び出されている最中に関数Yに呼び出されると戻る先が上書きされてしまうという問題点がありました。

それを解決したのがスタックでした。スタックは最後に入れたデータが最初に取り出される複数のデータを扱うためのデータ構造です。このデータ構造を用いることで戻る場所を複数保存できることができ、現在の関数処理が実現されました。

また、現在のプログラミング言語のほとんどではfor文では実現できない「再帰関数」という入れ子構造を扱うデータ処理を記述できるようになっています。

再帰関数の詳しい説明は下記記事で詳しく解説しています。

コードを見ながら理解する「入れ子構造」と「再起関数」

エラー処理

最初はgoto文で失敗した場合にエラー処理の場所に飛ばすことでエラー処理を実現していました。

しかし、それだとエラーが起こりそうな部分全てにgoto文を記述する必要があり可読性や漏れなどが発生しやすいという問題があります。

そこで用いられたのがエラーが起こりそうなコードを囲うという方法が使用されるようになりました。そして、finallyを導入することで出口を一つにすることでより強固なエラー処理を実現することができるようになりました。

おかしくなったらすぐに報告すべきという設計思想を「フェイルファースト」と呼びますが、例外的状況は言語によって異なっており、言語開発者何を大切にしているかによって変化します。

システムによって、停止することが許されない場面もあるかもしれませんが、開発段階などで間違いにすぐに気づくことができるというのはメリットでもあります。

名前空間

名前の衝突を防ぐために、領域に名前をつけたり、スコープを用います。

スコープには動的スコープ静的スコープがあります。

動的スコープはどこからでも変数の対応表にアクセスできてしまいます。この動的スコープの問題点は変数を書き換えてから別の関数を呼び出すと、その呼び出した関数に影響を与えてしまうことでした。

これを解決するために静的スコープが存在します。静的スコープは関数ごとに対応表を分けることでグローバルの変数に影響を与えません。また、ローカルなスコープに変数が存在しない場合は遡ってグローバルな対応表を見にいきます。

一見静的スコープには問題がないように見えますが、静的スコープにもネストして入れ子になっているように見える関数でも実際には入れ子になっていない問題や、外のスコープへの再束縛の問題などいくつかの問題を抱えています。

並行処理

プログラミングとハードウェアの並行処理は異なる概念です。

プログラミングの中での並行処理にはプロセスやスレッドなどが挙げられます。

基本的にはプログラミングの並行処理は、高速に処理を切り替えて同時にやっているように見せています。その切り替えは、協調的マルチタスクプリエンプティブルマルチタスクを使って実現しています。

  • 協調的マルチタスク
    • 切りの良いところでタスクを切り替える。つまり、切り替えができるサインが出るまで同じ処理を続ける
  • プリエンプティブルマルチタスク
    • 時間でタスクを切り分ける。個々のプログラムより権限のあるタスクスケジューラによって、強制的にタスクを切り替える

競合状態を防ぐために

並行処理を実装する上で切り替えを正しく動く状態で実装することは難しいものでした。

並行処理を実現するためには処理単体が問題なく動作すること以外にも考慮すべき「競合状態」がありました。

例えば、プリエンプティブルマルチタスクによって、強制的にタスクを切り替えられることで、原子性を必要とする銀行のシステムなどではバグが起こることがありました。

このようなシステムのことをスレッドセーフでないシステムと呼ばれます。

そもそも競合を引き起こす条件として3つが挙げられます。

  • 2つの処理が変数を共有している
  • 少なくとも1つの処理がその変数を書き換える
  • 一方の処理が一段つく前位にもう一方の処理が割り込む可能性がある

この3つの状態を避けるためには以下の方法が考えられました。

  • メモリを共有しない、書き換えない
  • 協調的なスレッドを使う(ファイバー、コールチン、グリーンスレッド)
  • 割り込まれると困る処理には印をつける(ロック、ミューテックス、セマフォ)

この割り込まれると困る処理に印をつける方法「ロック」にも問題がありました。

それはデットロックが発生してしまうことです。それは処理の順番によっては、お互いがロックを解除する状態を待ち続けてしまい、処理が停滞してしまうことです。

この問題を解決するためにトランザクショナルメモリがあります。トランザクショナルメモリは、手元で変更を実施してみて変更有効かを確認し、有効な場合には変更を保存し有効でない場合は最初からやり直します。

この方法を使えばロックを用いなくても問題なく並行処理が実現できます。

まとめ

この本では、言語に依存しない普遍的な知識を解説しています。プログラミング言語の歴史となぜその技術が生まれたのかを理解することで現在用いられている技術にも問題が多くあることがわかってきました。

この本ではそれぞれの技術の触りについてのみ述べられており、深い話はされていません。

それでも、技術全体をざっくり学ぶには良い本だと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です