世界を動かす技術を、日本語で。

七つのプログラミングの原言語 (2022)

概要

  • プログラミング言語学習の本質は 基礎的なパターン の理解
  • 世界の主要な言語群は 7つの「元言語(ur-languages)」 に分類可能
  • それぞれのur-languageは 独自の思考様式・記述法 を持つ
  • 新しい言語の学習難易度は 同系統かどうか で大きく異なる
  • 各ur-languageの特徴・代表例・歴史を簡潔に解説

主要なプログラミング元言語(ur-languages)とその特徴

  • プログラミング言語選び で迷う人が多いが、実は 根本的な発想やパターン が重要
  • ほとんどの言語は 7つの元言語 のどれかに系譜を持つ
  • 代表的な元言語とその特徴を以下に整理

ALGOL系(命令型言語の祖)

  • 代入・条件分岐・ループ を中心とした構造
  • 関数 による整理、 モジュール・例外処理・多態性 などの拡張も多い
  • C, Java, Python, C++, Fortran, Pascal, Ada, JavaScript, Ruby など多数がこの系統
  • 歴史 :Ada Lovelaceの解析機関プログラムから始まり、Fortran・COBOL・ALGOL 60の登場で確立
  • 特徴 :他の言語からの機能取り込みも多く、最も普及している系統

Lisp系(リスト処理・マクロ言語)

  • 括弧で囲む前置記法リスト構造 が基本
  • プログラム自身がリスト(データ)として扱えるため、 マクロによる言語拡張 が容易
  • 代表例: Common Lisp, Scheme, Clojure, Emacs Lisp, AutoLISP
  • 歴史 :1958年John McCarthyによる誕生。AI分野で一時隆盛。AI冬の時代も生き残る
  • 特徴 :言語自体の構文や意味を自在に拡張できる柔軟性

ML系(関数型言語)

  • 関数が第一級値Hindley-Milner型推論 による強力な型システム
  • 反復処理は 再帰、もしくは高階関数(map, foldなど)で表現
  • 代表例: Standard ML, OCaml, Haskell, Agda, Idris
  • 歴史 :定理証明支援のために開発。ヨーロッパで普及
  • 特徴 :型安全性、遅延評価(Haskellなど)、型拡張の発展

Self系(オブジェクト指向言語)

  • 全てがオブジェクトメッセージ送信 による動作
  • クラス の有無でSmalltalk(クラスあり)とSelf(クラスなし)に分岐
  • 代表例: Smalltalk, Self, JavaScript(Self系のクラスレスモデル)
  • 歴史 :Xerox PARCでSmalltalk誕生、Selfでクラスレス化。JavaのJITもこの流れから
  • 特徴 :オブジェクト間の動的な振る舞い変更、ライブ環境での開発

Forth系(スタック言語)

  • スタックベース の逆ポーランド記法(RPN)
  • 関数(ワード)定義 や構文自体の再定義が容易
  • 代表例: Forth, PostScript, Factor, Joy
  • 歴史 :1970年に天文台制御用として誕生、組込み系やプリンタ制御で普及
  • 特徴 :小規模・高速・自己拡張性に優れる

APL系(配列言語)

  • 全てがn次元配列記号的な高階演算子 による表現
  • コードが 非常に短く抽象的 になりがち
  • 代表例: APL, J, K, Q
  • 歴史 :1960年代に数学的配列処理のため開発、金融や統計分野で活躍
  • 特徴 :大量データの高速処理、独特な記号体系

Prolog系(論理型言語)

  • 事実と規則 を宣言し、 論理推論 で目的を達成
  • プログラムは 命題論理・述語論理 の記述
  • 代表例: Prolog, Mercury, Datalog
  • 歴史 :1972年フランスでAI研究の一環として誕生
  • 特徴 :探索・推論処理に強み、AI・知識表現分野で利用

ur-languageごとの学習・応用のポイント

  • 同系統の言語 同士は、習得コストが低く スムーズな移行 が可能
  • 異系統の言語 は、 根本的な思考パターンの切り替え が必要で、学習コストが大きい
  • 基礎パターン (ループ、再帰、データ構造操作など)を身につけることが最重要
  • 言語選択 に迷うより、まず どの系統かを意識して始めること が大切

まとめ

  • プログラミング言語は 見た目が違っても、根底にある考え方(ur-language)が重要
  • 自分が習得したい分野や用途 に合った系統を選ぶことで、効率的な学習が可能
  • 基礎パターンの習熟 が、どんな新しい言語にも応用できる普遍的な力となる

Hackerたちの意見

ブルース・テイトの「7週間で7つの言語」のアプローチをちょっと思い出すな。そこで初めてErlangに出会ったんだ。歴史的な観点から見ると、COBOLやFortranをALGOLファミリーの一部として説明するのはちょっと無理があると思うけど、すべての歴史が簡略化されるっていうことを思い出させてくれる良い例かもね。

COBOLは生きた化石ってこと? それに、今日のFortranはALGOL系のプログラミング言語からの水平遺伝子移動を受けたFORTRANファミリーなんだ。

FortranとAlgol、どっちが古いか知ってる人いる? Wikipediaによると、どちらも1957年頃に開発されたみたい。デザインに重なりはあったのかな?

もう一つの言語のクラスを追加したいな。証明を表現するための言語、Curry-Howard対応を通じてね。Leanがその代表例だと思う。これは関数型言語のサブクラスと見なせるかもしれないけど、別のクラスとして扱うほど違うかもしれない。特に、これらのプログラムの目的はチェックされることで、実行は二次的なものなんだ。

定理証明と複雑な型は、普通の言語に対する拡張みたいなものだね。 - AgdaやIdrisは複雑な型を持つ関数型言語 - IsabelleやLeanは複雑な型と読みにくいインタラクティブな証明を持つ関数型言語 - Dafnyなどは定理やヒントを持つ命令型言語 - ACL2は定理やヒントを持つLISP それに関連して、型クラスは実質的に関数型/命令型言語上の論理プログラミング(Rustのトレイトのように、https://rustc-dev-guide.rust-lang.org/traits/chalk.htmlで言及されてる)だね。

LeanはAgdaやIdrisと同じく、依存型のML系言語だから、「ML」でカバーできるね。Leanの長期的な目標は「実行は二次的」じゃないし、Microsoftは本気でソフトウェアを作ろうとしてるのが明らかだよ: https://lean-lang.org/functional_programming_in_lean/Program... 逆に「証明を表現するために意図されている」と強調したいなら、Prologがそれをカバーしてるから、Leanは半分ML、半分Prologと見なせるね。この観点から見ると、カリー・ハワード対応は論理に対する特定の計算アプローチを選ぶための実装の詳細に過ぎないよ。

それはMLから直接派生したものだね。

他にもいくつかのセマンティックファミリーがあるよ:Verilog、ペトリネットやそのバリエーション、カーンプロセスネットワークやデータフローマシン、プロセス計算、リアクティブ、項書き換え、制約ソルバー/定理証明器(Prologとは違うよ)、確率的プログラミング、そして7つのカテゴリーにぴったりはまらない新しい(実際に生産-readyな)言語たち:Unison、Darklang、Temporal Dataflow、DBSP。上記の言語を挙げるのはちょっとズルしてる気がするけど、ほとんどが通常のフォン・ノイマンマシンのセットアップに平行してるからね。でも「フォン・ノイマンを超えた計算方法」についての記事を書こうと思ってたんだ。

Sussmanの伝播器もチェックする価値があるよ。[0] [0] 伝播器のアート(今はURLがダウン中)

「項書き換え」 大学でスプレッドシートソフトを作ることになったんだけど、私は数式パーサーをやることにしたんだ。面白そうな挑戦だと思って。1週間悩んで、数式を自分がパースできる形に書き換えられることに気づいたんだ。だから、1+1をADD(1,1)に書き換える感じで。正規表現を学ぶのは拒否したから、パースのコードは「面白い」ものになったよ ;) 同僚のコメントを思い出す。「オッケー、アンディが動くって言ってるから、触らないでおこう。」XD 別のグループのやつは正規表現を使ってて、そいつの解決策は俺の20倍短かった。

「しばらくの間、『(フォン・ノイマンを超えた)計算する方法をすべて』に関する記事を書こうと思ってた。」これを読むのがすごく楽しみだ。とりあえず、スティーブ・イェッゲの記事の一部を引用するね:--- コンピュータの本質 その本を読みながら気づいたことは、私がコンピュータサイエンスの学位で受けたほとんどのコースは、ジョン・フォン・ノイマンが発明したか、彼の仕事をほとんど知的でない方法で基にしているってこと。どこから始めよう?フォン・ノイマン以前は、電子計算機は計算機だけだった。彼は現代のコンピュータを発明して、実質的にユニバーサル・チューリングマシンをシミュレートした。直列デバイスの方が並列デバイスよりも安くて早く製造できると感じたからだ。私たちが学んだ学部のマシンアーキテクチャのコースの80%以上は、彼のプログラム可能なコンピュータの設計に関する最初の報告からそのままだった。本当にあまり変わってない。彼は高速計算ユニットを持つ直列命令デバイスを作ったが、メモリは限られていてデータ転送は遅い(いわゆる「フォン・ノイマンのボトルネック」として知られている。まるで彼が過去60年間、他の人がもっと良いものを考え出せなかった責任があるかのように)。実際、ジョンはニューロンのようなセルオートマトンに基づいた動作する並列コンピュータを考案する途中だった。もし1957年に54歳で癌で悲劇的に亡くならなければ、1965年には生産に入っていたかもしれない。フォン・ノイマンは自分の直列コンピュータの限界をよく理解していたが、実際の問題を解決する必要があったので、それを実現するために必要なものをすべて発明した。機械命令を数値としてエンコードすること、固定小数点演算、条件分岐、反復とプログラムフロー制御、サブルーチン、デバッグとエラーチェック(ハードウェアとソフトウェアの両方)、バイナリから十進法への変換アルゴリズム、そして問題をモデル化するための数学的および論理的システム(それによって彼の計算機で解決できるか近似できるように)。 -スティーブ・イェッゲ、毎日の数学 https://archive.ph/6tOQF

S9スキームでの論理プログラミング: https://www.t3x.org/amk/index.html コードは本を買わなくても手に入るし、Simply Schemeや他の本で学んで、そのコードの関数を使えば、ソルバーは本当に理解しやすいよ。

さらに新しい(実際に生産準備が整った)ものもあるね。生産準備が整ってないと思うけど、実際には使われている:ChatGPTとか。もちろん、これがプログラミング言語かどうかは議論の余地があるけど、なんでそうじゃないって言えるの?決定論的じゃないけど、プログラミング言語にそれが必須だとは思わないし、人間がコンピュータに何をさせるかを伝えるために使われてるんだよね。

コンピュータサイエンスを勉強してた時(TU Delft)の一番好きな科目は「プログラミング言語の概念」っていう名前だった。CやScala(関数型用)とJavascript(プロトタイプ)を学んだんだ。それがあったおかげで、数年後にElixirを学ぶのがずっと楽になったよ。あと、基本的にUnreal Tournamentをプレイするエージェントをプログラミングするコースもあって、GOALっていうPrologベースの言語を使ってた。何年もPrologを使いたいと思ってたけど、どうやって使うか分からなかった。結局、LLMが生成するひどいパピアメントを修正するためのスペルチェックを作ったんだ。

私もそこにいたよ!o/ アンリアルトーナメントは、今まで見た中で一番クールなものだった。私の後の年にサービス終了したと思う。(今は他のところと同じような退屈なAIしかないし!)プロログの良い使い道は見つけてないけど、あんまり頑張ってないからね。GOALの方がずっと感動したことを認めるよ。でも最近まで、もっと「普通」の言語で全体を再現できるって気づかなかったんだ(それには多くの利点がある)。ドゥフ!

このGOALのこと? https://en.wikipedia.org/wiki/Game_Oriented_Assembly_Lisp

大学で似たような授業を受けたことがあるけど、教授がちょっと微妙だったけど、受けてよかったと思ってる。他の基本的な言語について薄っぺらい理解があるだけでもめっちゃ役立つし(アセンブリに関しては特に)。実際に役立つことはできないけど、少なくともドライバーの存在を知っていれば「ハンマーしか持ってないと、すべての問題が釘に見える」って罠にはまらずに済むよ。

記事の分類に対して一つ訂正したいことがある。Rubyはオブジェクト指向言語であって、ALGOLじゃない。インスピレーションはSmalltalkから来ていて、標準ライブラリの名前の多くもその流れから来てる(例えば、mapじゃなくてcollect)。Rubyは根本からオブジェクト指向なんだ。すべて(本当にすべて)がオブジェクトで、メソッド呼び出しはオブジェクトにメッセージを送ることとして考えられてる。RubyはよくPython(ALGOL)と比較されるけど、進化のルートは全然違ってて、エコシステムの同じポイントに収束してるんだ。RubyはPythonの唾を吐くラクダに対して、かわいいアルパカみたいな存在だと思ってる。

Pythonが新しいスタイルのクラスを導入してから、純粋なOOP言語になったよ。最初の「Hello World」レベルではそう見えないかもしれないけど、すべてのプリミティブ型がオブジェクトになったからね。OOP嫌いの人にこれを指摘するのが好きなんだ。>>> type(42) >>> dir(42) ['abs', 'add', 'and', 'bool', 'ceil', 'class', 'delattr', 'dir', 'divmod', 'doc', 'eq', 'float', 'floor', 'floordiv', 'format', 'ge', 'getattribute', 'getnewargs', 'getstate', 'gt', 'hash', 'index', 'init', 'init_subclass', 'int', 'invert', 'le', 'lshift', 'lt', 'mod', 'mul', 'ne', 'neg', 'new', 'or', 'pos', 'pow', 'radd', 'rand', 'rdivmod', 'reduce', 'reduce_ex', 'repr', 'rfloordiv', 'rlshift', 'rmod', 'rmul', 'ror', 'round', 'rpow', 'rrshift', 'rshift', 'rsub', 'rtruediv', 'rxor', 'setattr', 'sizeof', 'str', 'sub', 'subclasshook', 'truediv', 'trunc', 'xor', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'is_integer', 'numerator', 'real', 'to_bytes']

キャメルってパールのことじゃない?

特定の原言語を「オブジェクト指向」として特定する選択が、みんなを混乱させると思う。だって、OOは手続き型と同じようにプログラミングのスタイルに過ぎないから。PythonとC++が両方とも多重継承を持っているから同じ種類の言語だって言うのはあまり意味がないと思う。それは単なる観察できる共通点で、デリーとラスベガスがどちらも暑すぎるって気づくのと同じ。そうだけど、それが同じ種類の場所だからってわけじゃないと思う…

似たようなことを書いたよ: https://fmjlang.co.uk/blog/GroundBreakingLanguages.html Algol、Lisp、Forth、APL、Prologには同意するね。画期的な関数型言語としては、SASL(セントアンドリュース静的言語)があって、これはMLの少し前に開発されたものだよ。それからオブジェクト指向言語としてはSmalltalkがあって、これはSelfの前に出てきたんだ。他にもFortran、COBOL、SNOBOL(文字列処理)、Prograph(ビジュアルデータフロー)も、いろんな意味で画期的だったよ。

SelfがSmalltalkの代わりにリストに入ってる理由が分からない。Smalltalkが先に出たし、Alan Kayが「OOP」って名前を考えたんだよね。それに、MLはLispの子供と見なされてるし。

前の議論: https://news.ycombinator.com/item?id=35813496

2023年5月4日、念のために言っとくね。コメントは323件。あと2021年9月30日には29件のコメントがあったよ: https://news.ycombinator.com/item?id=28704495>。

TuftsのPLコースで最初の4つの言語(命令型、Lisp、ML、Smalltalk)のミニバージョンを作ったんだ。それが今、教科書として出版されてるよ [1]。残念ながら、Prologの部分はカットされちゃったけど。[1]: https://www.cambridge.org/ir/universitypress/subjects/comput...

プロログ部分のバージョンがインターネットアーカイブに出てくるかも?

ここにいる古参たちは、Prologのバリアントであるmakeに慣れてるよね。複雑なMakefileに苦しんだことがある人は、もっとまともな宣言型言語があればよかったと思ってるはず!

六つのプログラミング言語のメモリモデルを思い出すな: https://canonical.org/~kragen/memory-models/