宣言的プログラミング言語のフィードバック、リソース、および情報
質問
私は新しい言語の根底にあるいくつかの概念を考えてきました。最初は一種のおもちゃでしたが、今では本当に何かを意味するのではないかと思っています。この質問を投稿してオーバーフローをスタックして、それが以前に行われたかどうか、そしてフィードバック、アイデア、またはその他の情報を取得できるかどうかを確認しています。
私は主に読んだ後にこれについて考え始めました 宣言的プログラミングに関するジョナサン・エドワードのプレゼンテーション. 。それから私はそれを私の古いアイデアのいくつかと、私が現代の言語で見たものと混ぜました。
宣言的なプログラミングの背後にある主なアイデアは、「What」vs. "How」です。しかし、私は何度も聞いたことがあるので、ほとんどの場合「興味深い」という言葉のように思えます。
ジョナサン・エドワードのバージョンでは、彼は最初に強調することから始めました 怠zyな評価. 。つまり、これにはいくつかの興味深い結果があります 機能的リアクティブプログラミング(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)
コンパイラに純粋なものと何がそうでないかを伝えることで、プログラムを大幅に簡素化できます。別のポイントは、熱心または可変データです。ジョナサン・エドワードは、入力を変異可能で熱心であると認識していましたが、出力は機能的で怠zyであると認識していました。基本的に、 新しい入力が与えられた場合、プログラムは原子状態の変更を定義しました, 、 その後 出力は単に現在の状態の関数です. 。これが重要である理由を知りたい場合は、プレゼンテーションをご覧ください。入力は不純です。怠zyな評価は、原子状態の変化を定義するのに役立ちます。プログラムが手続き的にどのように書かれるかを見てみましょう。
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
キーワードによれば、入力は怠zyではなく、熱心だと言います。それでは、「原子状態の変化」がそれを表す方法を見てみましょう。
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のために、条件付きブランチとループのためだけにあることがわずかしかないことに気付いたときのようなものだと思います。そのため、彼らは言語のために、そしてラベルとgotoが非推奨になり、コンパイラと人々が何が起こっているのかを知ることができるようになりました。私たちが使用するもののほとんどは、そのプロセスから来ています。
スレッドに戻ると、これについて良いことは、スレッドがより柔軟であることです。コンパイラが自由にやりたいことを自由に行うことができれば、私たちが望むことを言うことに近づいてきたので、それをやりたい方法ではありません。そのため、コンパイラはマルチコアおよび分散プロセッサを利用できますが、優れたスレッドサポートなしでプラットフォームを補償します。
私が言及したい最後のことが1つあります。そして、それがテンプレートに関する私の見解です。これは、私がプログラミングを始めたとき(実際には約2年前)発達を開始した概念的な卵のようなものでしたが、その後、割れ始めました。基本的には抽象化の原則でしたが、クラスやオブジェクトよりもはるかに伸びていました。
それは私が関数をどのように知覚したかに関係していました。例えば:
int add (int a, int b)
{
return a + b;
}
わかった、 add
返された int
, 、しかし何 だった それ?それはある種のように感じました int
起こるのを待っています。いくつかのピースのないパズルのように。限られた可能性があり、特定のピースのみが適合しましたが、あなたが終わったとき、あなたは完成した製品を持っていて、他の場所で使用できるようにしました。これは、私が言ったように、抽象化の原則です。以下は、私が抽象化 +欠落していると思うものの例をいくつか紹介します - >具体的な関係:
- 関数 +引数 - >値
- 抽象クラス +メソッド - >クラス
- クラス +インスタンス値 - >オブジェクト
- テンプレート +引数 - >関数またはクラス
- プログラム +入力 +状態 - >出力
それらはすべて密接に関連しています。これは利用できるようです。しかし、どうですか?繰り返しますが、それがこれが問題です。しかし、怠zyな評価はここで興味深いです。 その作品がまだ欠けています 何か他のものに。コンパイラにとって、それは主に名前を不純物に抑制する問題です。上からの私の例のように:
(x ^ 2) + 3x + 5
(4 ^ 2) + 3x + 5 = 16 + 3x + 5 = 21 + 3x = 3(7 + x)
コンパイラを提供するピースが多いほど、それを完了し、プログラムをその重要なコアに削減できます。そしてその add
上記の機能は、外部のリソースに依存しているため、自動的にコンパイル時間に解決されます。コンパイラがどれほど賢いかに応じて、多くのクラスやオブジェクトを解決することができます。
それは今のところすべてです。これらのことの例をすでに見たことがあるなら、私は見たいです。また、アイデア、革新、リソース、またはフィードバックがある場合は、それも感謝しています。
解決
あなたは間違いなくを見てみたいです ハスケル プログラミング言語。
Haskellは非常に宣言的であり、怠zyな評価は組み込まれており、機能的な反応性プログラミングライブラリさえ存在します。しかし、最も顕著なのは、Haskellです 純粋に機能的です, 、つまり、すべて、本当にすべてです ピュア.
ですから、問題は、HaskellがどのようにIOを通じて発生する必要な不純物に対処するかです。
答えは、あなたが提示した考えに非常によく適合しています。 Haskellは、呼ばれる数学的構成要素を使用します モナド それは基本的に関数とともに何らかの価値を生成する計算を表します bind
(>>=
INFIX演算子として)、そのような計算をシーケンスします。
IOの例を考えてみましょう。行を読んで名前を読んで名前を出してください... IOでさえ純粋であるので、単に何かを実行することはできません。代わりに、より大きなIO計算を構築します
do
putStr "Enter your name: "
name <- getLine
putStrLn ("Hello " ++ name)
非常に不可欠に見えますが、ボンネットの下では、それは単なる構文です
(putStr "Enter your name: ") >>
(getLine >>= \name ->
putStrLn ("Hello " ++ name))
これを定義できます bind
/>>=
にとって 任意の種類の計算 何らかの形であなたが好きです。したがって、実際にあなたが話したことはすべて、このように実装することができます - FRPでさえ。
StackoverflowでMonadsまたはHaskellを探してみてください。このトピックには多くの質問がありました。そして、結局のところ、それはまだすべてのタイプチェックされているため、コンパイラによって正確さを実施することができます。