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

Introducing Rholang!

rholang sdk 0.1

rholang sdk 0.2

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)\)

3. 小结

可以看出,rholang中并没有函数调用,一旦给出初始值,比如前面的$x!(1) | u!(2) $,程序将自动运行,通过消息来协调多个process,并输出结果。
posted @ 2018-03-10 09:01  qxred  阅读(1349)  评论(0编辑  收藏  举报