Rholang的消息机制与元组空间
1. 消息机制
在上一篇《初识Rholang》中提到了Rholang通信机制的两个基本操作:
for( x <- channel ) P
从channel
这个name中读取一条消息并删除,消息保存到x
这个name中后执行P
channel!(Q)
将Q
这个process发送到channel
这个name中
Rholang的name(quoted process)用于收发消息,所以有时候也称name(quoted process)为channel。
这里可以将一个name视为一个信箱,如下。
它的工作方式如下:
- 信箱取件的顺序并不保证和存入顺序一致。
for( x <- channel ) P
表示从channel
这个“信箱”取件后继续执行P
,P
在这里表示一个process,这个process在取件后调用,因此称为continuation。
特别需要注意的是,如果在取件时信箱为空,调用者并不会等待。相反,调用者将P
这条指令存入到该信箱中。- 同样,在执行
channel!(Q)
往信箱中放入Q
时,会首先检查信箱中是否有continuation
。如果有的话,并不需要将Q
存入信箱中,只需将continuation
取出后之间与Q
匹配继续执行。 - 因此,信箱中保存的即有可能是continuation
P
, 也可能是Q
。
2. 合约
有如下代码:
new helloWorld in {
for( x <- helloWorld ){
@"stdout"!(*x)
}
}
按照上面的解释可以理解为:在helloWorld
这个信箱中放入指令{ @"stdout"!(*x) }
等待消息到达后执行。
如果向该信箱存入两条消息,比如helloWorld!("Hi") | helloWorld!("你好")
,执行结果如下:
可以看到,<-
监听后的continuation只会执行一次,这段代码要么输出"Hi"
, 要么输出"你好"
。
如果将上面的代码稍作修改,将<-
改为<=
,结果会如何?
for( x <= helloWorld ){
@"stdout"!(*x)
}
<=
操作符和<-
类似。唯一的不同在于在执行完continuation后,该continuation指令并不会从信箱中删除。
因此,可以看到,使用<=
监听的continuation每次有消息到达都会执行,有多少次消息到达就会执行多少次。
实际上这段<=
的代码还有另一种写法:
contract helloWorld(x) = {
@"stdout"!(*x)
}
换用contract
关键字后,代码执行结果与<=
并无差异。
比较上面的两张截图可以看到,contract
和<=
两段代码EVALUATING的结果完全一样,这说明contract
就是等价于<=
的语法糖。
那么contract
关键字定义的就是一个合约。所谓合约,即一段绑定到某个name的指令,这段指令在每次有消息到达时都会执行。
3. 一个稍微复杂点的例子
3.1 首先申明 add
、total
和get
三个name。
new add, total, get in {
}
可以将它们看成三个信箱,现在它们的状态都是空的。
3.2 然后在add
这个信箱中部署合约
contract add(@num) = {
if (num > 0) {
for( @current <- total ) {
total!(num+current)
}
}
}
现在add
这个信箱中就放入了该合约的指令,该指令的含义为:如果接受到的是一个正数,则将total
信箱中的数字取出然后加上该正数的结果存回total
信箱。
3.3 给total
信箱设置初始值
total!(0)
total
信箱中现在就有了数字零了。
3.4 监听get
信箱
for( rtn <- get ) {
for( @current <- total ) {
rtn!(current) |
total!(current)
}
}
现在get
信箱为空,因此它现在也存入了一条指令。这条指令含义是:如果有name被投递到该信箱,则将total中的值取出后发送给该name;同时也发送给total
恢复它的状态。
3.5 向add
信箱发送数字3
add!(3)
执行该语句会触发add
信箱中的合约指令,执行完该语句后,total
信箱中保存的数字变为了3
3.6 向add
信箱发送数字5
add!(5)
执行该语句会触发add
信箱中的合约指令,执行完该语句后,total
信箱中保存的数字变为了8
3.7 声明一个新的name
new return in {
}
3.8 将该name发送给get
信箱
get!(*return)
执行该语句会触发get
信箱的指令,执行完后,get
中的指令删除了,而return
信箱中有了数字8
3.9 完整代码
下面是完整代码,可以将它复制到https://rchain.cloud/执行。
new add, total, get in {
contract add(@num) = {
if (num > 0) {
for( @current <- total ) {
total!(num+current)
}
}
} |
for( rtn <- get ) {
for( @current <- total ) {
rtn!(current) |
total!(current)
}
} |
total!(0) |
add!(3) |
add!(4) |
new return in {
get!(*return) |
for( @v <- return ) {
@"stdout"!(v)
}
}
}
这里由于|
是并行执行的,对get
的调用相对于其它并行执行的process的顺序并不是固定的,所以如果你多试几次的话,每次得到的结果可能会不同。
4. 元组空间(Tuplespace)与可组合性(Compositionality)
从上面的例子可以看到
- Rholang的代码与数据合为一体,都存在于name中。
- Rholang代码的执行的最终结果即是name的状态变化。
这些name的集合即是元组空间(tuplespace),可以将它们想象成一组信箱、或者一个快递柜。
因此,Rholang在RChain上的执行的本质是元组空间的状态变迁。
假设有两台计算机,每台计算机上都维护了一个元组空间。
如果add
和get
这两个name由其中一台计算机负责维护,另外两个name由另一台计算机维护,即使它们来自于不同的tuplespace,但是对于代码的逻辑是没有任何影响的。
这也是RhoCalculus与传统的冯洛伊曼计算机最大的不同 - 可组合性(Compositionality)。
可组合性的意思是说,一台基于RhoCalculus的计算机和另一台基于RhoCalculus的计算机能够组成一个新的基于RhoCalculus的计算机。
而这对于程序本身是透明的!RChain通过可以无限叠加的RhoMachine来组成一个计算网络,而这个计算网络整体上就是一个大的RhoMachine!这就是RChain无限可扩展性的根源!
本文地址: https://blog.youkuaiyun.com/wangjia184/article/details/80277759 转载须注明出处
下一篇《名字空间》