通过例子和实践来学习rho语言。下面的例子和练习都很值得去运行、阅读、修改和完善。修改练习和教程中任何你感到有意思的代码,这样能够获得最好的学习效果。该教程包含了rho语言最常见以及最重要的特性,足以让开发者快速入门。 课程0 -- 开发环境 配置你的开发环境 为了可以运行这个教程里面的rholang代码,你需要一些开发环境。 这不是一个会让你感到疲惫的rholang开发工具或者技术栈。 然而它展示了一些基本的开发环境给你开始。 网上编译器 RChain社区的成员提供了一个基于公共网站的在线rholang编译器。 这个工具非常有前途,也是一种入门的简单方式。 但是它还是开发节点,有时候会不稳定。 本地节点 真正正确运行rholang代码的方法是在通过启动你自己本地机子的RNode然后使用它的rholang编译器。 首先你要为你自己的平台安装 RNode 对于初学者,这里有详细的一步一步指导你怎么使用AWS 或者Docker启动你的节点. 一旦你的RNode安装好了,你可以运行基本的独立节点。 $ rnode run -s -n 在单独的终端里,你可以在REPL模式下一次执行一行rholang。 $ rnode repl ╦═╗┌─┐┬ ┬┌─┐┬┌┐┌ ╔╗╔┌─┐┌┬┐┌─┐ ╦═╗╔═╗╔═╗╦
╠╦╝│ ├─┤├─┤││││ ║║║│ │ ││├┤ ╠╦╝║╣ ╠═╝║
╩╚═└─┘┴ ┴┴ ┴┴┘└┘ ╝╚╝└─┘─┴┘└─┘ ╩╚═╚═╝╩ ╩═╝ rholang $ Nil Deployment cost: CostAccount(0,Cost(0)) Storage Contents: for( x0, x1 <= @{Unforgeable(0x01)} ) { Nil } | for( x0, x1, x2, x3 <= @{"secp256k1Verify"} ) { Nil } | for( x0, x1 <= @{"sha256Hash"} ) { Nil } | for( x0, x1 <= @{Unforgeable(0x03)} ) { Nil } | for( x0, x1, x2, x3 <= @{"ed25519Verify"} ) { Nil } | for( x0, x1 <= @{"blake2b256Hash"} ) { Nil } | for( x0 <= @{Unforgeable(0x02)} ) { Nil } | for( x0 <= @{Unforgeable(0x00)} ) { Nil } | for( x0, x1 <= @{"keccak256Hash"} ) { Nil } rholang $ @"world"!("hello") Deployment cost: CostAccount(5,Cost(64)) Storage Contents: @{"world"}!("hello") | for( x0, x1 <= @{Unforgeable(0x01)} ) { Nil } | for( x0, x1, x2, x3 <= @{"secp256k1Verify"} ) { Nil } | for( x0, x1 <= @{"sha256Hash"} ) { Nil } | for( x0, x1 <= @{Unforgeable(0x03)} ) { Nil } | for( x0, x1, x2, x3 <= @{"ed25519Verify"} ) { Nil } | for( x0, x1 <= @{"blake2b256Hash"} ) { Nil } | for( x0 <= @{Unforgeable(0x02)} ) { Nil } | for( x0 <= @{Unforgeable(0x00)} ) { Nil } | for( x0, x1 <= @{"keccak256Hash"} ) { Nil } 当你运行更多行数的rholang代码时候,你可以使用RNode的eval模式来执行代码。 $ rnode eval intersection.rho Evaluating from intersection.rho Result for intersection.rho: Deployment cost: CostAccount(39,Cost(1132)) Storage Contents: @{Unforgeable(0xb19519ab773d1ec4ce96f1b71b748552e4a084dfc9942371717f5cb87e818879)}!(@{"name"}!(Nil)) | @{Unforgeable(0xb19519ab773d1ec4ce96f1b71b748552e4a084dfc9942371717f5cb87e818879)}!(@{"age"}!(Nil)) | @{"world"}!("hello") | for( x0, x1 <= @{Unforgeable(0x01)} ) { Nil } | for( x0, x1, x2, x3 <= @{"secp256k1Verify"} ) { Nil } | for( x0, x1 <= @{"sha256Hash"} ) { Nil } | for( @{{@{"name"}!() | _ /\ @{"age"}!() | _}} <= @{Unforgeable(0xb19519ab773d1ec4ce96f1b71b748552e4a084dfc9942371717f5cb87e818879)} ) { @{Unforgeable(0x00)}!("Both name and age were in the data") } | for( x0, x1 <= @{Unforgeable(0x03)} ) { Nil } | for( x0, x1, x2, x3 <= @{"ed25519Verify"} ) { Nil } | for( x0, x1 <= @{"blake2b256Hash"} ) { Nil } | for( x0 <= @{Unforgeable(0x02)} ) { Nil } | for( x0 <= @{Unforgeable(0x00)} ) { Nil } | for( x0, x1 <= @{"keccak256Hash"} ) { Nil } 有一些RNode的输出会出现在你运行代码的同一个终端。但是其它一些代码输出会直接出现在第一个终端。 所以在你熟悉什么输出出现在哪里前请确定好检查两边的终端。 Cryptofex IDE 一个叫做cryptofex 的开发环境已经进入了alpha版本。 Cryptofex可能最后最好的开发rholang的地方,但是现在还是很早期的软件。 Cryptofex提供rholang语法高亮特性并且可以在RChain集成节点上检测dApps。 IDE同时也提供环境创建和测试在以太网上,私人测试网上和单独模式的EVM上的智能合约。 课程1 -- 发送与标准输出(stdout) 发送与标准输出(stdout) 说声Hello
"Person waiving hello"
编程界有一个存在已久的传统——输出"Hello World"应该是你学习的第一个程序。下面是一个在屏幕上输出"Hello World"的最简单例子。 hello.rho 练习 请让程序输出"Rholang rocks!" 而不是 "Hello World"。 练习 尝试将"stdout"替换为别的语句。会得到什么结果? 尝试一下这个有趣的通道名称@"someChannel". 这里可以比较随意。请让程序在屏幕上输出 "Sup World"。 标准输出(stdout)到底是什么东西
Channels are like mailboxes for sending messages rho语言的核心是通道(channel,下面都称为通道)通信. 通道是你可以用来发送和接收消息的通信线路。你可以使用!字符来在通道中发送消息。
Redo this diagram! stdout 是一个特殊的通道,用于将文本发送至"标准输出",通常指你的电脑屏幕。正因为它的特殊,我们不得不将它写在第一段学习的代码里面。 使用其他通道
Sent messages wait to be received here in "message purgatory"... JK, it's called the "tuplespace" 实际上你可以在很多通道中发送消息,而非只有stdout。 但其它通道不像 stdout 他们不会在屏幕上显示。 tupleSpace.rho 那么,在其他通道中的消息将被发送至哪里?哪里都不会去!这些消息暂时哪儿都不去,这些消息会继续待在通道内,等待其他人去取出它们。我们将在下一课程中学习如何获取这些消息。同时,消息滞留所在的地方,我们称为 "元组空间"。 请确保你的信息保留在元组空间里。你应该会看到像下面的信息。 Storage Contents: @{"RandoChannel"}!("This won't be on the screen") | for( x0, x1 <= @{Unforgeable(0x01)} ) { Nil } | for( x0, x1, x2, x3 <= @{"secp256k1Verify"} ) { Nil } | for( x0, x1 <= @{"sha256Hash"} ) { Nil } | for( x0, x1 <= @{Unforgeable(0x03)} ) { Nil } | for( x0, x1, x2, x3 <= @{"ed25519Verify"} ) { Nil } | for( x0, x1 <= @{"blake2b256Hash"} ) { Nil } | for( x0 <= @{Unforgeable(0x02)} ) { Nil } | for( x0 <= @{Unforgeable(0x00)} ) { Nil } | for( x0, x1 <= @{"keccak256Hash"} ) { Nil } 同时做两件事
Rather than following an ordered list, all ingredients are added concurrently. Looks delicions 在rholang中,我们不会告诉计算机做完一件事,再到另一件。相反,我们会告诉它需要做的所有事情,然后"并行地"执行它们,或者一次性全部执行。 parallel.rho | 的发音是 "parallel", 可简称为 "par"。 练习 向"pizza shop"通道发送消息"1 large pepperoni please"。 练习 向"Mom's Phone"通道发送"Hi Mom"。 练习 用一个程序在屏幕上输出两个消息,"Rick"和 "Morty"。 小测试 stdout!("Programming!") 将在屏幕上输出什么? Programming! stdout! Nothing @"what"!("Up") 在什么通道上发送消息? @"Up" @"what" what rholang会先执行哪一条语句? @"stdout"!("Dogs") | @"stdout"!("Cats") 输出 "Dogs" 输出 "Cats" 都不。 它们是并行的 PS. 有一个特殊的通道 stderr. 请尝试一下看看往这个通道发送消息,会发生什么? 有什么区别? 课程2 -- 接收 消息检查
// Dear future self, keys in freezer because... 在上一章我们学习了如何发送消息。现在是时候学习如何接收消息了。常规语法如下: for(message <- channel){ // Do something here} 顺便提一下, // 用于标示注释。 //后面的内容程序并不会运行。写好注释可以有利于其他开发者(包括你自己)阅读代码,并了解代码的意图,其他读你代码的开发者会感激你写注释的。 通信事件
Pizza shop can receive messages on its channel. 下面的代码使用披萨店的通道发送了一个消息,披萨店收到了它。pizza店通过将消息打印至标准输出来表明其已收到。 pizzaOrder 练习 将上述消息发送至一个不同的通道,如@"coffeShop". 消息会被接收端打印出来吗? 还是东西留在了元组空间里么? Let's hit up the coffee shop. 练习 记住,在rholang中,任何事情都是并行地而非按顺序地执行。如果我们把接收信息的代码放在前面,那么披萨店的代码仍可执行。尝试一下吧。 元组空间污染 如果你遇到了旧数据滞留在元组空间并会对后面的代码执行有影响,你需要清空你的元组空间。最简单的方式是删除你的数据目录.rnode 使用上述方法清空元组空间已经过时了。一个更好的方法是防止它一开始被旧数据污染。我们可以通过修改最上面的new代码段来实现。 旧的方案 new stdout(rho:io:stdout) in { @"world"!("Welcome to RChain") } 尝试下面新的方案 new world, stdout(rho:io:stdout) in { world!("Welcome to RChain") // No more @ or " " } 我们将在“不可伪造的names”的课程中讲解它的原理。现在你不需要每次都重置通道。 发送前接收
Rather than the message appearing first, then someone receiving it, Greg is trying to receive first. Hopefully someone will send him a message so he can have a comm event. 当发送和接收同时存在于通道时,这被称为通信事件,或称为"comm event"。 不像普通邮件那样必须被发送,对方才能被接收,在rholang中,上述两个事件可以以任何顺序发生或者同时发生。这类似于可以先接收消息,再发送它。每当发送和接收共存时,就会触发通信事件。 合约
The poor chef is too busy making sure he can receive orders to take care of his pizza. 我们的披萨店例子很好地说明了通信事件,但期望每次有新的订单时,披萨店都能自动发出一个新的接收来处理它们,这并不现实。 幸运地是,我们可以只部署一次代码,然后每次接收到它的消息时都执行一次。这类代码称为“智能合约”。让我们看一个比披萨店更高级但相似的例子--咖啡店。 coffeeShop.rho 练习 在咖啡店点第二杯饮料 练习 更改上面例子的确认消息 一般来说,下列哪一个会第一个发生? 发送,因为它与普通邮件的工作原理一样。 接收,因为以该方式运行的代码更快。 发送或接收都可以最先发生,或者同时。 接收,因为rohlang是并行的。 都不。直接触发通信事件(comm event)。 练习 通道被命名为 @"coffeeShop"。将它更名为你所选择的特定咖啡店的名称。然后使用我们最近学到的new来修改代码 Persistent For 实际上,在rholang中有两种不同的语法来表示持续从通道取出信息。我们刚刚学习contract语法。下面的用for语法的代码是等价的。 contract @"coffeeShop"(order) = { for(order <= @"coffeeShop") { 注意,上述代码与正常的 for 不同,因为它使用了双划线 <= 而不是单划线 <-. for和contract是有不同的地方的,我们会在讨论区块链的时候讨论到他们的区别。现在你可以将它们当做同一功能。 练习 用持久的for语法而不是"contract"语法来写一个想咖啡店这样的披萨店合约。尝试自己从头写一次整个代码,这样会让你更容易记清语法。 下面哪一项是与其他两项不同的? for (a <- b){} contract b(a) = {} for (a <= b){} 哪一个发送语句会与for (message <- @"grandmasSnapChat"){Nil}对应产生一个通信事件 ? grandmasSnapChat!("Hi Grandma") @"grandmasSnapChat"!("Glad you're snapping Grandma") for("Here's a snap for you g'ma" <- @"grandmasSnapChat")
|