本文的代码可以复制到http://rchain.cloud/在线执行。
回顾计算机理论发展史,现今几乎所有软件都基于上世纪的理论:
这些理论奠定了当今主流的计算机模型-冯诺依曼计算机模型。发展到今天,即使在引入线程、纤程、mailbox、channel、异步等概念后,并没有从根本上改变其顺序执行的本质, 其在大规模分布式并行执行的环境下(比如区块链网络)相形见绌。
前人早已洞悉到了这些问题并逐渐发展出了一些以并行计算作为着眼点的形式系统。 其中以Actor模型和process-calculus最为著名。
RChain的分布式并行计算模型来自于Rho-caculus, 它是process-calculus的分支。它的发展脉络可以从下图中了解到。
RChain的编程语言Rholang衍生自Rho-calculus,自然地继承了Rho-calculus的四个C属性:
Rho-calculus可以用下面的项(term)进行表述
P, Q, R ::= 0
| for( ptrn1 <- x1; … ; ptrnN <- xN ) P
| x!( Q )
| *x
| P|Q
x, ptrn ::= @P
接下来一项一项对照代码解释。
假想一辆自动驾驶的汽车, 传感器监控着道路、速度、方向等参数, 行车电脑根据它们控制着方向、油门刹车。而它们又影响着转向机构、变速箱、发动机等等, 在它们的内部又是各种机械结构相互作用最终决定汽车的行进。
在这个复杂系统中,所有的参与者都是process, 而各部分通过消息相互作用,组成一个有机的整体。 以这样的眼光看世界的话,随会发现世界也是由通过消息相互影响的process构成。
在Rholang的世界中,亦是如此:
有如下代码
new myName in {
Nil
}
Nil
是最简单的process,它表示一个已经停止的process(就是啥都不做),对应rho-calculus中的0
new myName in
申明一个quoted process,也叫做name。其中myName
是这个name的标识符,对应rho-calculus中的 @P
。 它的作用域仅在后面的花括号范围内。name(quoted process)提供了消息的通信机制作为channel使用,可以将它视为一个无序队列(Unordered Queue)。
通过!
操作,可以向这个channel发送一个process作为消息。 比如如下代码将Hello World
发送到myName
中。发送的必须是一个process,而一切都是process,当然也包括字符串。
new myName in {
myName!("Hello World")
}
而通过<-
操作,可以从channel接收并删除一个消息,接收到的消息是一个name(quoted process)
new myName in {
for( v <- myName ) {
Nil
}
}
for
后面的花括号是一个在接受到消息后执行的process,称之为continuation。
在Rholang的世界中,是不存在顺序执行的。
如果需要先执行完A然后执行B,那么A和B之间一定有某种数据依赖,需要通过上面的消息机制实现。
一般地,通过P|Q
操作并行地执行多个process。 比如:
new myName in {
for( v <- myName ) {
Nil
} |
myName!("Hello World")
}
这段代码并行执行了两个process:其中一个process向myName
发送了”Hello World”字符串;另一个process从myName
读取一条消息后啥也不做。
上面讲到的几个基本操作实际上都继承自π-calculus。 那么剩下的两个操作,就是rho-calculus对π-calculus的发展。
在rho-calculus中, name(quoted process)是process的引用,它代表的是指向的process的代码,亦既process的语法结构(syntactic structure),
可以简单地类比为磁盘上的可执行文件;
而process是一个可运行的实例,类比于执行文件启动后创建的进程,是一个动态的概念。
通过*
和 @
两个操作符可以让name(quoted process)和process相互转换,如下图。
*x
Reify操作将name(quoted process)反序列为process@P
Reflect操作将process序列化为相应的name(quoted process)例1:
@"stdout"!("Hello World")
如前文所说,一切都是process。那么"stdout"
和"Hello World"
都是process。将”Hello World”这个process发送给”stdout”肯定是不行的。
因为根据x!(Q)
,发送的目的必须是一个name。因此@"stdout"
操作将”stdout”这个process转换成了相应的name。实际上这就是一个系统合约:使用标准输出返回消息。
例2:
new myName in {
for( v <- myName ) {
@"stdout"!(*v)
} |
myName!("Hello World")
}
从myName
中获取的消息以v
标识,根据前面的文法for( ptrn1 <- x1; … ; ptrnN <- xN ) P
,v
一定是一个name。
而接下来需要将v
发送到@"stdout"
,又根据x!(Q)
发送的必须是process而不能是name,因此需要*v
装换成了相应的process。
例3:
new myName in {
for( @v <- myName ) {
@"stdout"!(v)
} |
myName!("Hello World")
}
这段代码和例2的代码非常类似。不同的是,接受到的消息用@v
标识符表示,那么这里v
是一个process,因此可以直接发送给@"stdout"
*x
和@P
两个操作的加入让rholang具备了反射的能力。这里所说的反射不同于JAVA/C#程序员熟悉的元编程(meta-programming)。
rholang的反射是一种过程化的反射,它使得process具有迁移到网络上其它节点执行的能力,因此Rho-calculus还有个别名叫移动进程演算(Mobile process calculus)。
例4:
new myName in {
for( v <- myName ) {
*v
} |
myName!({ @"stdout"!("Hello World") })
}
这段代码将{ @"stdout"!("Hello World") }
这个process发送到了myName
,另一个process在接收到后执行该process。
需要注意的是myName!({ @"stdout"!("Hello World") })
和for( v <- myName ) { *v }
这两个process是可以在不同的网络节点上执行。
但是对于rholang程序开发者是透明的。这就是rholang的可迁移性(Mobility),它使Rholang程序在RChain的网络上“流动”,具体的细节在后文名字空间一节中详述。
转载请注明出处
本文地址: https://blog.csdn.net/wangjia184/article/details/80090924
下一篇《元组空间》