声明性编程语言的反馈,资源和信息
题
我一直在想一些新语言的概念。起初这是一个玩具,但是现在我想知道这是否真的意味着什么。我要发布此问题以堆叠溢出,以查看它是否以前已经完成,以及是否可以获取任何反馈,想法或其他信息。
我主要在阅读后开始考虑这一点 乔纳森·爱德华(Jonathan Edward)关于宣言节目的演讲. 。然后,我将其与一些旧的想法以及我在现代语言中看到的内容混合在一起。
声明性编程背后的主要思想是“什么”与“如何”。但是,我听说过很多次,所以它似乎几乎总是像“有趣”一词,它实际上并没有告诉您任何令人沮丧的东西。
在乔纳森·爱德华(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)
因此,在这里,如果输入更改,则值将自动更改。在我最喜欢的一种语言中, 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)
通过告诉编译器什么是纯粹的,什么不是,我们可以简化我们的程序很多。另一点是渴望或可变的数据。乔纳森·爱德华(Jonathan Edward)的意见是可变和渴望的,但输出是功能性和懒惰。基本上, 给定新的输入,该程序定义了原子状态更改, , 进而 输出将只是当前状态的函数. 。如果您想看看为什么这很重要,请参阅演示文稿。输入是不纯的。懒惰评估有助于定义原子状态变化。让我们看一下如何按程序编写程序:
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的东西时,这就是这样的事情:有条件的分支和循环。因此,他们在然后建造了如果是else,以及用于语言,标签和戈托的弃用,编译器以及人民可以分辨发生了什么。我们使用的大部分来自该过程。
回到线程,这是一个好事是线程更灵活。如果编译器可以自由地做自己想做的事情,因为我们更接近说我们想要的东西,而不是我们想要做的。因此,编译器可以利用多核和分布式处理器的优势,但仍能在没有良好线程支持的情况下补偿平台。
我想提到的最后一件事。这就是我对模板的看法。这是一个概念性的鸡蛋,我开始编程(实际上是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
上面的功能将在编译时间自动解决,因为它不依赖于外部资源。甚至可以解决许多课程和对象,以及大部分程序,具体取决于编译器的智能程度。
目前为止就这样了。如果您已经看到这些事情已经完成的示例,我想看看。而且,如果您有任何想法,创新,资源或反馈,我也很感激。
解决方案
您肯定想看看 哈斯克尔 编程语言。
Haskell是非常声明性的,懒惰的评估是内置的,甚至功能性反应性编程库也存在。但最值得注意的是,哈斯克尔是 纯粹的功能, ,即一切,实际上一切,都是 纯的.
因此,问题是Haskell如何处理通过任何IO产生的必要杂质。
答案非常适合您提出的想法。 Haskell使用一种称为的数学结构 单子 这基本上代表了产生一些值以及功能的计算 bind
(>>=
作为Infix Operator),对此类计算进行序列。
因此,让我们以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即可;这个主题有很多问题。毕竟,它仍然是所有类型检查的所有内容,因此可以由编译器执行正确性。