プログラミング言語・パラダイムの変遷・興隆について考えてみました。
|
一般的に流行したプログラミング言語の変遷というより、自分が使ってきた言語の変遷というべきかな^^;
自分はプログラミング言語の歴史や言語そのものに詳しいわけでもないので、ここに書いているのは完全に自分の想像。
特に「どの言語が流行っているか」なんてものは多分に主観が入るので^^;
でも大枠で見れば、自分が勉強してきたプログラミング言語は世間の認識とそれほどずれてはいないと思う。
すなわち、手続き型言語→構造化プログラミング→オブジェクト指向言語→関数型言語。
○○という概念(パラダイム)が出来て、それに沿ったプログラムの作り方をするのが○○プログラミング。
○○プログラミングをやりやすくする為のコンピューター言語が○○言語。
例えば“オブジェクト指向”という概念が提唱され、“オブジェクト指向プログラミング”を行う。それを支援するのが“オブジェクト指向言語”。
例えば、オブジェクト指向で「オーバーライドされたメソッドを呼び出す」という概念は、継承関係のあるクラス群の中で同一名称のメソッドをテーブルに入れておき、テーブル内で一番下層のメソッドを呼び出す…というようにすれば、オブジェクト指向言語でなくても実現できる。
(この例は、C++のvtableをC言語で実現するイメージ)
だからC言語でオブジェクト指向プログラミングをすることは出来るが、しかしC言語がオブジェクト指向プログラムを作りやすいかと言ったら、そんな事は無い。オブジェクト指向言語の方がオブジェクト指向プログラミングをしやすい。
そういう訳で、当ページでは「○○プログラミング」と「○○言語」は不可分・同じ意味として、あまり区別せず適当に使用する。
人間が一番分かりやすいのは手続き型言語だと思う(笑)
やりたい処理(手順)を順番に記述していく。
そもそも一般的に「プログラム」と言えば、「運動会のプログラム」だの「演奏会のプログラム」だの「テレビの番組表」だの、内容と順序を表したものだし。
アルバイトに来た人や新入社員に最初に教えるのは仕事のやり方(手順)じゃないかな?
つまりそれが一番分かりやすいから。
設計書も手続き型で(やりたいことを手順化して)書いてあることが多いと思う。
現在のコンピューターも、CPU(マシン語)レベルでは手続き型で処理している。
CPUが出来る事は、データ入出力(レジスターとメモリー、あるいは外部とのI/O)・条件判定・分岐(ジャンプ・goto)くらい。
それをどういう順番で処理するか書くのがアセンブリ言語レベルのプログラミングで、これは手続き型だと思う。
(実際のCPUはもっと高機能で、命令をパイプラインで分割して並列実行したり分岐で先読み実行したりと、必ずしも書かれた手順通りに実行しているわけでもないようだが)
もうちょっと進んで高級言語になっても、基本はアセンブリ言語と変わらない。
変数への代入・I/Oなどの処理と条件判定(IF文)と分岐(goto)だけあればプログラムは書ける。(サブルーチン呼び出しはgotoの一種)
しかしgotoが無秩序に多用されると混乱を招く(プログラムがスパゲッティー化する)し、グローバルな変数に自由に読み書きできると意図せぬ所で値を書き換えることが出来てバグの温床になるし(デバッグが困難だし)。
という訳で構造化プログラミングという考え方が出てきた。
構造化プログラミングとは何か?をきちんと説明できるほど覚えているわけではないんだけど(苦笑)
おおまかに自分がイメージしているのは、
サブルーチン化は、ひとつの処理が長々と何行にも及ぶことを防ぐ。
サブルーチン自体は特定の目的の処理に特化することになるので、分かりやすい。
また、サブルーチンを呼び出す側もすっきりして処理の意図が分かりやすくなる。
(極端に言えば、詳細設計書の一行(手順の1つ)毎にひとつのサブルーチンを作ってもいいと思う)
グローバル変数を使わないプログラミングというのは、サブルーチン(メソッド・関数)に引数を渡し、サブルーチンは引数だけを使って処理し、サブルーチンから返ってくる値だけを使うということ。
つまり「副作用の無い」プログラミングということ。
サブルーチン内でグローバル変数を書き換えるのは「副作用」ということになる。
(グローバル変数が意図せぬ場所で書き換えられるのを防ぐという観点から、副作用の無いプログラミングが望ましい)
ここで重要なのは、「副作用の無い」プログラミングは関数型言語の特徴として語られることが多いけれども、実際は構造化プログラミングのレベル(時代)でも大事だということ。
だから自分は、「副作用が無い(ことを目指す)」のを関数型言語(だけ)の特長だとは思っていない。
「オブジェクト指向」と言った場合、大きく「オブジェクト指向設計」と「オブジェクト指向プログラミング」に分かれると思う。
オブジェクト指向が流行した頃(≒Javaの黎明期)は、設計もオブジェクト指向で行うという考え方があった。
業務に登場する人やシステムをオブジェクトと捉え、それらがどうやりとりするか(相互作用・メッセージ)を割り出すという感じ。
例えば銀行にお金を預ける業務を考えてみる。
登場するのはお客さんと銀行員(銀行)。預金業務は、お客さんの財布からお金を減らし、銀行の口座にお金を加算する。
なのでオブジェクトは顧客と銀行で、メソッド(メッセージ)は「預金する」。設計完了。
じゃぁここからプログラムのオブジェクトを導き出せるか?と言うと、答えはノー。
顧客クラスとか銀行クラスを作っても、何の役にも立たない。
実際のプログラムに関して言えば、必要なのは銀行員がシステムに入力する為の画面とか、入金をするハードウェアから金額を受け取るインターフェース、そして口座DBを書き換えるライブラリーといった感じだろう。
コンピューターシステム(オブジェクト指向プログラム)に必要なオブジェクトは、画面オブジェクトや金額を受け取るオブジェクト、DBを管理するオブジェクトという具合。
だから業務分析にオブジェクト指向分析(ユースケース図とか)は役に立ったのかもしれないが、オブジェクト指向プログラミングには全く無関係。
オブジェクト指向プログラミングにおけるオブジェクト(クラス)の抽出は、完全にプログラム都合であって、業務は関係ない。
「オブジェクト指向入門」がオブジェクト指向分析(設計)を指しているのかオブジェクト指向プログラミングを指しているのか区別しないのは混乱の一因だと思う。
(まぁ、ユースケースがコンピューターシステム内部まで含めれば、プログラムのオブジェクトまで抽出できるのかもしれないけど…)
ちなみに、オブジェクト同士がメッセージを送りあう(メッセージパッシング)という考え方は、関数型言語のサンプルによく出てくるアクターモデルの方が近いと思う。
オブジェクト指向用語の「メッセージパッシング(メッセージの送信)」は、オブジェクト指向言語では「メソッド呼び出し」のこと。
クラス(オブジェクト)や継承の例と言うと、動物と犬・猫だの図形と三角形・四角形だのという例が出されることがある。
間違ってはいないけど、実業務のプログラミングには役に立たないんだよな(苦笑)
自分がオブジェクト指向プログラミングで一番重要だと思うのは、フレームワーク(枠組み)を作れるという点。
例えばJavaのアプレットのプログラミングでは、プログラマーはAppletクラスを継承したクラスを作り、paint()メソッドをオーバーライドして自分の処理を記述する。
サーブレットならServletやHttpServletクラスを継承し、doGet()だのservice()を実装する。StrutsならActionクラスを継承してexecute()を実装する。
そのメソッドがどこから呼ばれるか…という事は、プログラマーは気にしない。呼び出す手順は定められており、プログラマーは必要な部分だけを記述すればよい。
呼び出されるメソッドの枠(インターフェース)だけ決めておき、オーバーライドすることによって個別の処理が変えられる…というのはオブジェクト指向の仕組みを使っている。
例えばバッチ処理をするプログラムを考えてみよう。
処理内容は個々のバッチによって当然異なるが、開始メッセージと終了メッセージは必ず出すものとする。
すると、バッチの共通クラスは以下のような形になる。(Java)
public abstract class Batch { public void execute() { //各バッチはこのメソッドからスタートするものとする doInit(); //初期処理 doMain(); //主処理 doEnd(); //終了処理 } protected void doInit() { System.out.println("start"); //開始メッセージ出力 } protected abstract void doMain(); //各プログラマーはこのメソッドをオーバーライドして処理本体を書く protected void doEnd() { System.out.println("end"); //終了メッセージ出力 } }
この程度のフレームワークを作れないと「オブジェクト指向言語のプログラマー」とは言い難い。
フレームワークというか、単なる小さなクラス設計だし。
実際のところこんなの簡単で、複数の似た処理を作る際に、同じ部分があったら共通クラスを作ってそちらにメソッドを定義するだけ。
異なる部分は抽象メソッド(処理本体は記述しないメソッド。C++でいう純粋仮想関数)にして個別の具象クラスで書く。オブジェクト指向の用語で言えば多態だのポリモーフィズムだのという機能を使っていることになるが、そんなの知らなくても出来るしw
staticおじさんプログラムを作る人達は、こういったフレームワークの概念も知らないんだろうなぁ。
オブジェクト指向プログラミングでもうひとつ重要なのは、責務の分離やカプセル化とかいうやつ。
まぁ普通はこちらが先に言われる利点だと思う。(→構造体からクラスへの変遷)
クラス内にメンバー変数を定義しておくが、その変数にアクセスできるのはクラス内のメンバー関数(メソッド)だけで、外部からはアクセスできないようにする。
構造化プログラミングでグローバル変数にアクセスしないことを目指しても、オブジェクト指向でない言語では、グローバル変数アクセスを拒否することは出来ない。
オブジェクト指向言語では、クラスという範囲を定義することで、言語として判別することが出来る。(エラーを出すことが出来る)
オブジェクト指向というかJava限定の話になってしまうかもしれないが、JavaにはJUnitという自動テストツールがある。
作ったプログラム(メソッド)を呼び出し、期待した結果になっているかどうかをチェックしてくれるライブラリーだが、
JUnitでテストしやすいメソッドは、実は副作用の無いメソッド。引数のみに依存し、オブジェクトの内部状態(フィールドの値)やDBアクセス等の副作用が無い方がテストを作りやすい。
「副作用が無いプログラミング」というのは、Java(オブジェクト指向言語)でも重要なのだ。
オブジェクト指向プログラミングで難解なのは、やはりメソッドがオーバーライドできる事だろう。
実行時の具象クラスによっては、今自分が見ているソースのメソッドは実行されないかもしれない。
これを防ぐ為にオーバーライドを禁止する構文があるが、それを付けすぎると逆に融通が利かなくなってしまう。困った事だ…。
ところで、構造化プログラミングは、オブジェクト指向プログラミングになったからと言って不要になるわけではない。
クラス内部で考えると、メンバー変数(Javaではフィールドと言う)はクラス内のグローバル変数と同じ。クラス内のどのメソッド(メンバー関数)からもアクセスできるから。
また、サブルーチン化しないでひとつのメソッド内に大量の処理を記述することだって出来るし、そんな事をしたら保守性が下がる点も変わらない。
つまり、オブジェクト指向言語と言っても、構造化プログラミング(の技法)は依然として必須。
適切にサブルーチン化(メソッド分割)し、メンバー変数(フィールド)を不必要に使用しないようにすべき。
Javaでよくあるアホなコーディング規約に、「メソッドには全てJavadocコメントを付けよ」というものがある。
(少なくともオーバーライドされたメソッドは、オーバーライド元にJavadocコメントが付いてればいーじゃん…)
構造化プログラミングを推し進めるとメソッドが増えるので、全てにコメントを付けると手間が馬鹿にならない。この面倒を嫌ってメソッド分割せず、一メソッド内に大量に処理を記述する人が出てきて、可読性が悪くなり、保守性が下がる。
一目見て内容が分かるようなメソッドであれば、特にコメントは要らないと思う。(というか、コメントが無くても分かるようにすべき)
→コメント不要論
関数型言語自体は自分が学生だった頃より前からあるので、歴史は長い。
でも最近改めて注目を集めている理由は、大量データを処理しなければならないのが現実化してきており、その為には分散(並列)処理するのが現実的だが、
その為のデータの取り扱いが関数型の考え方だと都合が良いから。
なんだけど、自分としては、一番気に入っているのはコレクション操作をメソッドチェーンで記述できること。
例として、数値が文字列で入っているリストに対し、Intに変換して奇数だけを抽出して合計を算出して変数sに入れるとすると、こんな感じ。(Scala)
val s = List("123", "456", "789").map(_.toInt).filter(_ % 2 == 1).sum
mapはリストの各要素を変換した新しいリストを作り、filterは条件に応じて抽出した新しいリストを作る。
sumは全要素を合計するメソッド(関数)で、よく使いそうな処理はこのように提供されている。
(このようにメソッドを次々とつないでいくからメソッドチェーンと呼ばれているのだと思う。
最初に例の説明として挙げた文章の通りのプログラムになるところが素晴らしい)
このmapやfilterには、“変換処理や抽出の条件”すなわち“関数”を引数として渡している。
関数型言語は関数を引数として渡せる(値として扱える)のが一番の特徴だろう。(関数型言語って呼び名だし)
もちろんC言語やC++には関数ポインターがあるので、関数(処理)を渡すことは出来る。
Javaでも(Collections.sort()の様に)“処理を渡す”という概念は実現できる。しかしインターフェースを定義し、それを実装したクラスを作り、さらにインスタンス化して渡す必要があり、お世辞にも便利とは言えない。
オブジェクト指向プログラミングはオブジェクト指向言語で書くのが便利なのと同様、関数プログラミングも関数型言語の方が書きやすい・見やすい。
(JavaもJavaSE8からラムダ式(関数のようなもの?)を導入しようとしているが、ただ単に関数を渡せるようにしただけでは大して便利ではなく、コレクションもそれに応じたメソッド(つまりmapやfilter等)が用意されないと意義が薄れると思う)
上記の例で出てくるmap・filter・sumは、内部的にはリスト内に入っている順番で処理する必要は無い(結果だけ順番通りになっていればいい)。
例えば前後に2分割してそれぞれ処理を行い、最後に結合して返すようになっていても問題ない。
つまり並列処理することが出来る。
ここら辺が関数型言語が並列(分散)処理に向いていると言われる所以だと思う。
そして並列・分散して処理させる為には、共有しているデータ(グローバルなデータへのアクセス)は無い方が楽。
つまり副作用が無い方がよい。
という訳で関数プログラミングは副作用が無いプログラミングを突き詰めているので、関数型言語の特徴に挙げられるんだと思う。
自分が学生の頃は、関数型言語(Lisp)の次の世代の言語は論理型言語(Prolog)という論調だった気がするけど、どう?^^;
最近Coqという言語名をちらほら目にするけど、よく分かりません(爆)
(「証明型言語」というのはここでのタイトルにしたいが為の造語です)
LL言語とは、関数型都市名古屋における新しい定義では言語名や説明あるいは使っている人の名前にLが2つ入っている言語のことだがw、
以前の旧態依然とした定義では、軽量言語(Lightweight Language)のこと。「LL言語」と言うと「language」と「言語」が重複しているが、まぁよくあることだ^^;
「何が軽量?」というのは「スクリプト言語の定義」と同じくらい曖昧な気がするが、まぁ手軽・スピーディーに記述できる言語ってところかなぁ。
コンパイルが必要だったり、型をしっかり書かなくてはいけなかったり、という事に対するアンチテーゼらしい。
個人的には、その場で書いて使い捨てる(対話型ツールでの入力とかの)ようなものだったらガチガチに堅い文法は邪魔(大文字小文字の違いも許容して欲しい位)なのでLL言語でもいいと思うが、
そうでないものは、きっちり定義を書いてエラーをコンパイラーに摘出させる方が好み。(チェックは機械にやらせた方が確実)
人や環境によって重視することが違うので、どの言語が一番かなんて事は決められない。
優等生的な答えとしては、使用目的に応じて一番便利な言語が使えれば一番良い。
とは言え、いくつもの言語を覚えるのは大変(文法をさらうだけならまだしも、その言語らしいプログラムを作ろうと思ったらどうしても経験が必要になる)。
1つの言語で何でも出来るなら、その言語だけ覚えたいというのも分からなくはない(苦笑)
現時点ではそんな究極で至高な言語は無いと思うけど。
ただまぁ、「こういうプログラムなら分かりやすい」という類型と、それを実現しやすい言語というのはありうるかもしれない。
どんなプログラミング言語にせよ、一番分かりやすいプログラムは、分岐が一切無いプログラム。頭から順番に処理していって、終わり。
フローチャートを描いても縦一直線になるだけなので、そもそもフローチャートなんぞ不要!(図とか絵とか描くの苦手です、はいorz)
オブジェクト指向言語なら、(クラスの粒度によるけれども、)異なる部分を別クラスにすることにより、メソッド呼び出しを一直線に出来る。
(クラスは増えちゃうけど、オブジェクト指向言語でクラスを作ることをためらってどうする(苦笑))
class Main {
public void execute(int arg) {
doInit();
//主たるメソッド内で分岐している
if (arg == 1) {
doMain1();
} else if (arg == 2) {
doMain2();
} else {
throw new RuntimeException();
}
doEnd();
}
private void doInit() { 〜 }
private void doMain1() { 処理1 }
private void doMain2() { 処理2 }
private void doEnd() { 〜 }
}
|
→ |
class Main {
public void execute(int arg) {
//主たるメソッド内は一直線
Sub sub = Sub.getInstance(arg);
sub.execute();
}
}
abstract class Sub { public static Sub getInstance(int arg) { //このメソッド内には単純な分岐しか無い //(処理が1つしか無い) switch(arg) { case 1: return new Sub1(); case 2: return new Sub2(); default: throw new RuntimeException(); } } public void execute() { //主たるメソッド内は一直線 doInit(); doMain(); doEnd(); } private void doInit() { 〜 } protected abstract void doMain(); private void doEnd() { 〜 } } class Sub1 extends Sub { @Override protected void doMain() { 処理1 } } class Sub2 extends Sub { @Override protected void doMain() { 処理2 } } |
この場合、各メソッドがMain内のフィールドにアクセスしていたら、 メソッドをSubクラスに移動させたとき、フィールドも何とかする必要がある。 JavaならSubクラスをMainクラス内に(インナークラスとして)定義すればMainクラスのフィールドにもアクセスできる。 が、そもそもMainがフィールドを持たず、メソッドには全て引数で渡していれば(副作用の無いようにコーディングされていれば) 特に気にせずそのまま別クラスに移動させられる。 |
関数型言語も、一直線になるよう考慮されている気がする。
例えばScalaでMapから値を取り出す(キーが存在していればその値、無ければデフォルト値にする)のは以下の様に書ける。
var v = "" if (map.contains(key)) { v = map.apply(key) } else { v = "default" } |
→ |
val v = if (map.contains(key)) { map.apply(key) } else { "default" } |
→ |
val v = map.get(key) match { case Some(v) => v case _ => "default" } |
→ |
val v = map.getOrElse(key, "default") |
手続き的な書き方 | Scalaのifは値を返せる | match式による条件判定 | ScalaのMap(コレクション)には 便利なメソッドが色々用意されている |
リストの処理でfilterやmapを使うのも、ループ処理が表面上出てこなくなる、つまり処理が一直線に記述できる。
val s = List("123", "456", "789").map(_.toInt).filter(_ % 2 == 1).sum
今までさんざん書いてきた通り、構造化プログラミングでもオブジェクト指向プログラミングでも、もちろん関数プログラミングでも、副作用の無いプログラムの方が色々と便利。
とは言ってもプログラムは外部との入出力(ファイル・DBや画面との入出力・表示等)が必ずあるので、副作用が全く無いプログラミングなんて実用上はありえないけど。
(オブジェクト指向言語なら、責務の範囲内でフィールドに値を持たせる方が便利なケースもあるし。例えばDBアクセスクラスなら、どのメソッドでも使用するコネクションオブジェクトはフィールドに持っていた方が便利だろう)
Scalaの場合、変数宣言にはvarよりもvalが推奨される。
varで宣言された変数は後から値を代入する(値を変更する)ことが可能で、valだと初期値から変更することが出来ない(Javaのfinalと同じ)。
しかしvalでオブジェクト(インスタンス)の変数を定義しても、そのインスタンスの内部状態の変更を禁止することが出来るわけではない(変更するメソッドが提供されているなら、それを呼び出せる)。この辺りはC++の方がきっちりしていて、C++ではメンバー関数にconstを付けると副作用のある処理は禁止(コンパイルエラー)になる。
ただ、これはデバッグログ出力なんかも副作用ありとして使えなくなってしまうので、微妙な場合もある(苦笑)
※と思っていたら、ログ出力は書ける模様。@PG_kuraさんに教えていただきました。→例 [2011-11-20]純粋関数型言語は全然知らないんだけど、クラスとか無いだろうから、こういう問題は無いのかな?
プログラムの補足をするコメントはあってもいいと思うけど、過度なコメントは逆に可読性を落とすのでやめた方がいい。
// 変数を宣言する int n; // nに1を足す n += 1;
↑アホか。見れば分かることをいちいち書くのは、書くのも手間だし見るのも冗長なだけ(読み飛ばさないといけないし、全体の行数が増えてしまうので、一度に見える範囲が減ってしまう)。
どうせコメントを付けるなら、せめて変数nが何を意味しているのか・どういうつもりでその処理をしているのかを書くべき。
と言うよりも、意味していることが分かる変数名や関数名・メソッド名を付けるべき。そうすれば、コメントを付ける必要は無い。(逆に、意味を表現しづらい内容ならコメントを付けるべき)
プログラミング言語も「言語」なので、(コメントが無くても)何をしているかは分かるように表現できるはず(ある程度は)。
boolean b = execute(); if (b == false) { 〜 } |
→ |
boolean success = execute(); if (!success) { 〜 } |
→真偽値を比較するのはやめるべき |
むしろコメントを書くなら、「なぜそうコーディングしたか」の理由の方が重宝する。(複雑なロジックとか排他制御を気にしないといけない場合とか)
ただ、英語圏の人にとっては、メソッド名や変数名も英語なのでコメントを(英語で)書くのは完全に冗長だけど、日本ではちょっと事情が違うかもね。
int max; //最大値 int 最大値; //最大値
↑変数名に日本語が使えたら、このコメントは完全に冗長だということがよく分かると思う。
英語圏の人にとっては、英語の変数名やメソッド名もこんな感じなのかもしれないなぁ。だから、英語圏の人がコメント不要を言うのはある意味当たり前かも^^;
(日本人でも英語が出来る人なら同じかもしれないが…)
メソッド(関数)に付けるコメントも、場合によっては無くしていいと思う。
それは、内部的に使用するメソッドで、メソッド名(や実装内部)を一目見れば処理内容が想像できる場合。
これはプログラミング言語の文法の話になるけど、ソースの見栄えがどうなるかも重要な要素。
変数aに1+2+3を計算して代入するのに「LET a 1 PLUS 2 PLUS 3 END」みたいな書き方をする言語があったら、最悪だよね(爆)
BASICからC言語に移った当初はC言語の記号に苦労したけど、今は逆にBASICとかの方が冗長に見える。
例えばブロックを表すのに「{ 〜 }
」と書くか「begin 〜 end
」と書くか。加算を書くのに「n +=
1」と書くか「n = n + 1」としか書けないか。
計算や代入を書くのにいちいち「COMPUTE」とか「MOVE」とか書かないといけないCOBOLとか
代入の記号に「:=
」を使うPascalとか
インデントに意味を持たせるPythonとか
は好きじゃないんだよな〜。
Pascalってどちらかと言えば理論先行の言語で、「=」は等号だから値の代入を意味するのはおかしいってんで「:=
」を使ってるわけだよね?
でもプログラムで出てくるのは等値比較より代入の方が断然多い訳で、多い方にキー入力量が多いキーワードを割り当てられるのは苦痛だ。
自分はC言語以来、C++・(仕事で)PL/I→Java・JavaScriptと、ほぼC言語系の文法で来た(ExcelVBAとかSQLも覚えたけど)ので、それが一番分かりやすい。
だからJavaからScalaへ入るのは分かりやすかった。(だからScalaのSIP-12とか、最悪だw
なんで文法をBASICに似せようとするのか(爆))
関数(メソッド)の定義や呼び出しも「f(x)
」「f(x, y)
」という形式がいい。
F#ってどんなのだろうと思ってほんのちょっとだけ見てみたが、「f x
」「f x y
」ってのは違和感がある…。
まぁこういうのは慣れの影響が大きいので、慣れてくれば違和感は感じなくなることがほとんどだけどね(笑)
(Scalaでもメソッド定義がdefだったのは最初は違和感あったもんなー)
(Lispは最初から突き抜けているので、違和感を感じる暇も無いw)
コンピューター言語自体やコンパイラーにも数学的理論による裏付けがあったりするが、
でも個人的には、理論優先で作られた言語よりも、利便性優先の言語の方が好きかも。(利便性優先の為に文法の整合性が無茶苦茶だったとしたら、それはそれで覚えにくくて困るけど)
BASIC・アセンブリ言語・C言語程度しか知らなかった頃はもっと便利な言語を作りたいと思ったこともあったけれども、なんと浅はかだったことでありましょうかorz
コンピューター言語の進化は、現状の言語の問題点を解決する為に新しい言語が出てきていると思う。
とは言え、パラダイムは完全に置き換わるという訳でもなく、以前の言語(考え方)でも良いものは残るはず。
自分が知る範囲で一番大きなパラダイムは、構造化プログラミング・オブジェクト指向プログラミング・関数プログラミング。
構造化プログラミングはオブジェクト指向プログラミングでも必須なので、残るはオブジェクト指向と関数型。
関数プログラミングは勉強し始めたばかりで詳しくないんだけど、
オブジェクト指向プログラミングで一番重要だと思っているフレームワーク(枠組み)作りや責務を限定することは
クラスを持たない関数型言語ではどうやって実現するんだろう?
という訳で、今のところは、オブジェクト指向と関数型を融合させた“オブジェクト指向関数型言語”Scalaが最強!w