私は、新しい言語の根底にあるいくつかの概念について考えてきました。最初はオモチャのようなものでしたが、今は本当に意味があるのだろうかと思っています。この質問を Stack Overflow に投稿して、以前に行われたかどうか、フィードバック、アイデア、またはその他の情報を入手できるかどうかを確認します。
私がこれについて考えるようになったのは、主に宣言型プログラミングに関する Jonathan Edward のプレゼンテーションを読んだ後です。それから私はそれを私の古い考えのいくつかと私が現代言語で見たものと混ぜ合わせました.
宣言型プログラミングの背後にある主な考え方は、「何を」対「どのように」です。しかし、それは何度も聞いているので、ほとんど「面白い」という言葉のように、実際には何も語らず、もどかしいものです。
ただし、ジョナサン・エドワードのバージョンでは、最初に遅延評価を強調することから始めました。これには、関数型リアクティブ プログラミング (FRP)という興味深い結果があります。これは、アニメーション付きの FRP の例です (私が作成した構文を使用):
x as time * 2 // time is some value representing the current time
y as x + (2 * 500)
new Point(x, y)
したがって、ここでは、入力が変更されると値が自動的に変更されます。私の好きな言語の 1 つであるDでは、「純粋な」関数と「不純な」関数が区別されていました。純粋関数は、外界との接続がなく、他の純粋関数のみを使用する関数です。そうでなければ、それは不純です。ポイントは、純粋な関数が与えられた引数に対して同じ値を返すことを常に信頼できるということでした。
ここでも同様の推移的な原則が適用されると思います。私たちの不純物はtime
です。time
、存在x
、したがってy
、およびしたがって、によって触れられるものはすべてnew Point(x, y)
不純です。しかし、通知(2 * 500)
は純粋です。ご覧のとおり、これはコンパイラにその制限がどこにあるかを伝えます。数式を変数で単純化するようなものだと思います。
(x ^ 2) + 3x + 5
(4 ^ 2) + 3x + 5 = 16 + 3x + 5 = 21 + 3x = 3(7 + x)
何が純粋で何がそうでないかをコンパイラに伝えることで、プログラムを大幅に単純化できます。もう 1 つのポイントは、熱心なデータまたは変更可能なデータです。ジョナサン・エドワードは、入力は変更可能で積極的であるが、出力は機能的で怠惰であると認識しました。基本的に、新しい入力が与えられると、プログラムはアトミック状態の変更を定義し、出力は単に現在の状態の関数になります。これが重要な理由を知りたい場合は、プレゼンテーションを参照してください。入力が不純です。遅延評価は、原子状態の変更を定義するのに役立ちます。プログラムが手続き的にどのように書かれるかを見てみましょう。
void main ()
{
String input = "";
writeln("Hello, world!");
writeln("What's your name? ");
input = readln();
writeln("Hello, %s!", input);
writeln("What's your friends name? ");
input = readln();
writeln("Hello to you too, %s!", input);
}
ここでのbind
キーワードは、変更があった場合に次のコードが実行されることを示していますbegin
。このmutable
キーワードは、入力が遅延ではなく積極的であることを示しています。それでは、「原子状態の変化」がそれをどのように表すかを見てみましょう。
program:
mutable step := 0
bind begin:
writeln("Hello, world!")
writeln("What's your name? ")
++step
bind readln() as input when step = 1:
writeln("Hello, %s!", input)
writeln("What's your friends name? ")
++step
bind readln() as input when step = 2:
writeln("Hello to you too, %s!", input)
ここで、プログラマーにとってより簡単に、より読みやすくすることができるものを見てきました。まず第一に、醜いstep
変数と、それを毎回どのようにインクリメントしてテストする必要があるかです。以下は、新しく改善されたバージョンがどのように見えるかの例です。
program:
bind begin:
writeln("Hello, world!")
writeln("What's your name? ")
bind readln() as input:
writeln("Hello, %s!", input)
writeln("What's your friends name? ")
yield // This just means the program jumps to here instead of at the beginning
writeln("Hello to you too, %s!", input)
halt
その方がいいです。ただし、完璧ではありません。でも完璧な答えを知っていたら、私はここにいないでしょう?
ゲームエンジンを使用したより良い例を次に示します。
class VideoManager:
bind begin: // Basically a static constructor, will only be called once and at the beginning
// Some video set up stuff
bind end: // Basically a static destructor
// Some video shut down stuff
class Input:
quitEvent as handle // A handle is an empty value, but can be updated so code that's bound to it changes.
keyboardEvent as handle(KeyboardEvent) // This handle does return a value though
mouseEvent as handle(MouseEvent)
// Some other code manages actually updating the handles.
class Sprite:
mutable x := 0
mutable y := 0
bind this.videoManager.updateFrame:
// Draw this sprite
class FieldState:
input as new Input
player as new Sprite
bind input.quitEvent:
halt
bind input.keyboardEvent as e:
if e.type = LEFT:
this.player.x -= 2
else if e.type = RIGHT:
this.player.x += 2
else if e.type = UP:
this.player.y -= 2
else if e.type = DOWN:
this.player.y += 2
これには、コールバック、イベント、さらにはループなども必要なく、スレッドが明らかであることが気に入っています。何が起こっているのかが分かりやすくなり、Python のような構文だけではありません。言語開発者が、人々がラベルと goto を for で使用しているものは、条件付き分岐とループだけであることに気付いたときのようなものだと思います。そのため、if-then-else、while、for を言語に組み込むと、ラベルと goto は非推奨になり、コンパイラと人々は何が起こっているかを知ることができました。私たちが使用するもののほとんどは、そのプロセスから来ています。
スレッドに戻ると、これの良いところは、スレッドがはるかに柔軟であることです。私たちがやりたいことではなく、やりたいことを言うことに近づいたので、コンパイラがやりたいことを自由にできる場合。そのため、コンパイラはマルチコアおよび分散プロセッサを利用できますが、適切なスレッド サポートのないプラットフォームを補うことができます。
最後に 1 つ言いたいことがあります。そして、それがテンプレートに関する私の見解です。これは、私がプログラミングを始めたとき (実際には約 2 年前) に発達し始めた概念的な卵のようなもので、その後、割れ始めました。基本的には抽象化の原則でしたが、クラスやオブジェクトにまで及びました。
それは私が機能をどのように認識したかに関係していました。例えば:
int add (int a, int b)
{
return a + b;
}
わかりましadd
た、 が返されましたが、それはint
何でしたか? int
起こるのを待っているような気がしました。数ピースのないパズルのように。可能性は限られており、特定のピースしか適合しませんでしたが、完成したら、他の場所で使用できる完成品ができました。これは、私が言ったように、抽象化の原則です。抽象化 + 欠落部分 -> 具体的な関係と思われる例をいくつか示します。
- 関数 + 引数 -> 値
- 抽象クラス + メソッド -> クラス
- クラス + インスタンス値 -> オブジェクト
- テンプレート + 引数 -> 関数またはクラス
- プログラム + 入力 + 状態 -> 出力
それらはすべて密接に関連しています。これを活かすことができそうです。しかし、どのように?繰り返しますが、これが質問である理由です。しかし、遅延評価はここで興味深いものです。なぜなら、まだ欠落している部分を別のものに渡すことができるからです。コンパイラにとっては、ほとんどの場合、名前を不純物にまで逆参照することです。上記の私の例のように:
(x ^ 2) + 3x + 5
(4 ^ 2) + 3x + 5 = 16 + 3x + 5 = 21 + 3x = 3(7 + x)
コンパイラに提供する部分が多ければ多いほど、コンパイラはそれを完成させ、プログラムを本質的なコアに縮小することができます。上記のadd
関数は、外部リソースに依存しないため、コンパイル時に自動的に解決されます。コンパイラがどれだけ賢いかによっては、多くのクラスやオブジェクト、そしてプログラムの大部分を解決することさえできます。
それは今のところすべてです。これらのことの例がすでに行われているのを見たことがあるなら、私はそれを見たいと思います. また、アイデア、イノベーション、リソース、またはフィードバックがあれば、それもよろしくお願いします。