rholang学习(三) rholang
本文属作者原创,转载请注名出处
http://www.cnblogs.com/qxred/p/rholang.html本系列目录
rholang 学习(一) Lambda演算 Lambda Calculus
rholang学习(二) pi 演算 Pi Calculus
rholang学习(三) rholang
0. 参考文献
Rholang - The R-Chain Smart Contract Language
rholang 是 Reflective Higher Order language的缩写,是一种基于rho calculus (pi calculus的扩展)的并发编程语言。不同于现代主流的编程语言,rholang具有最原始的并发性,特别适合block chain这种高TPS的应用需求。
1. rholang语法
为了方便理解,我们从最核心的语法开始,逐步扩展。这个最核心的语法来自于前两个Youtube。
rholang中有两种符号:process,用大写字母表示;channel(或者叫name),用小写字母表示。
你可以这么理解:process是一段new thread的代码,可以执行,并在执行完之后销毁。而channel相当于传统语言的变量,用于存放数据。
\begin{eqnarray} P,Q ::=&& \mathbf{0} &\mathrm{//什么都不做} \nonumber\\ && \mathbf{for}(x \leftarrow y)P & \mathrm{//channel\, x等待接收channel\, y来的消息,之后执行P。注意y的消息到了x之后,y将不再持有该消息} \nonumber\\ && x!( P ) & \mathrm{// 将P的运行结果输出到channel\, x}\nonumber\\ && P | Q & \mathrm{// 同时执行 P,Q} \nonumber\\ && *x & \mathrm{// 将x当作一段process执行} \nonumber\\ x, y ::=&& @P & \mathrm{// channel的命名就是 @P的代码字符串} \nonumber \end{eqnarray}
Mike Stay对channel的解释是,你可以理解channel是一个电话号码,全是“数字”。而这些数字是由这些规则产生的,比如运用第一和最后一条规则,我们可以立即得到一个“数字0”
$@0$
通过运用第三条规则,我们可以得到“数字1”:
$@0!(0)$
进而得到“数字2”:
$@0!(0)!(0)$
所以可以看到,@P实际上就是P代码的字符串。比如,如果
$P= x+1 $
则$@P = "x+1"$
而*则是 @的逆运算:
$*(@(2+1)) = *"2+1" = 3$
2. 例子
例1 Hello World
这样我们可以写一段Hello World的代码:
new helloWorld in {
contract helloWorld(name) = {
"Hello, ".display(name, "!\n")
}
| helloWorld!("Joe")
}
这里 new helloWorld in { ... } 是套用了pi calculus中的语法
$(\nu x)P$
意思创建一个新的channel x 并绑定到P. 绑定的意思是,如果P执行完,会释放掉(就像释放一个线程一样),则该channel也销毁。
contract helloWorld(name) = {
意思是channel name等待helloWord channel的消息,相当于
\begin{eqnarray} && \mathbf{for} (name \leftarrow helloWorld) = \{\nonumber \end{eqnarray}
两者的区别是,根据Mike Stay的讲解,前者永远在执行,而后者只执行一次。
| helloWorld!("Joe")
是并发执行的另一个process,该process将"Joe"送入到helloWord channel。只要helloWord收到"Joe",则触发了第一个process,最终输出
"Hello, Joe!"
例2 Hello World Again
new helloAgain in {
contract helloAgain(_) = {
new chan in {
chan!("Hello again, world!") |
for (text <- chan) {
text.display()
}
}
}
| helloAgain!(Nil)
| helloAgain!(Nil)
}
输出
Hello again, world!
Hello again, world!
例3 set, get
new MakeCell in {
//创建一个新的channel
contract MakeCell(init, get, set) = {
//init, get, set是外界的channel,MakeCell监听并接收这三个channel,一旦到齐,则执行下面代码
new valueStore, setC, getC in {
//创建3个channel,这三个channel对contract之外的代码不可见
valueStore!(init) |
//将init中的值发送到valueStore channel
contract getC(_) = {
//channel getC等待调用(监听并接收Nil,然后执行下面的代码)
for(ack <- get; value <- valueStore) {
//get中的值传给ack, valueStore中的值(初始值或者上次set的值)
valueStore!(value) | ack!(value) | getC(Nil)
//valueStore!(value) 将value回填到valueStore,因为value <- valueStore之后,valueStore中的值就销毁了。getC(Nil)重启当前的getC,否则只能执行一次
}
} | getC(Nil) |
contract setC(_) = {
for(pair <- set; value <- valueStore) {
//pair <- set,set是一对channel: [newValue, ack]
match pair with [newValue, ack] => {
valueStore!(newValue) | ack!(Nil) | setC(Nil)
}
}
} | setC(Nil)
}
} |
//cell usage
new myGet, mySet in {
//私有channel,外界不可以访问
MakeCell(123, myGet, mySet) |
//建立一个初始值为123的MakeCell,
new ack in {
myGet!(ack) |
//将ack channel输出到myGet channel,这样就触发了前面的 for(ack <- get; value <- valueStore) {,于是valueStore=ack=123
for (result <- ack) {
//result从ack等待消息
result.display() |
//123
mySet!([456, ack]) |
//将[456, ack]填充set channel,触发了前面的for(pair <- set; value <- valueStore) {,其中value <- valueStore是被myGet!(ack)中的valueStore!(value)触发的
for (_ <- ack) {
//等待ack发来消息,也就是ack!(Nil)
myGet!(ack) |
//将ack发送给myGet,这时ack已经没消息了,这么做是为了触发getC中的for,等到valueStore!(456) 完成,就触发for(ack <- get; value <- valueStore), ack重新赋予456
for (result <- ack) {
result.display()
//456
}
}
}
}
}
}
所以结果是
123
456
例4 多process
contract C(x, u, z, a) = {
for (y <- x; v <- u) {
z!(*y + *v)
//执行y,v中的代码,比如y="1+2",v="7",那么将输出10到channel z
| P (x, z)
//同时执行P,不需要等前一步完成
}
| for (w <- z) {
//需要等z!(*y + *v)完成才执行这一步
a!(*w * 2)
//执行w的代码,并*2后,将结果输出到a
| Q(z w)
//同时执行Q,不需要前一步
}
}
现在做一个测试
P = Q = 0
new x, u, z, w, a in
x!(1) | u!(2) |
C(x, u, z, w, a) |
//w = z = 1+2 = 3, a = 6
for (r <- a) { println("the resulting value is " + r)
结果是
the resulting value is 6
例5 结果不确定的例子
contract C(x, u, z, a) = {
for (y <- x; v <- u) {
z!(*y + *v) | P (x, z)
}
| for (w <- z) {
a!(*w * 2) | Q(z w)
}
| for (w <- z) {
a!(*w * 2 - 1) | Q(z w)
}
}
这个例子中,\(z!(*y + *v)\) 执行完之后,后两个for同时被触发,所以不确定是执行了 \(a!(*w * 2)\) 还是 \(a!(*w * 2 - 1)\)