当前位置:网站首页>Pony语言学习(八):引用能力(Reference Capabilities)
Pony语言学习(八):引用能力(Reference Capabilities)
2022-08-10 05:29:00 【溴锑锑跃迁】
(如果你有更好的翻译,请务必联系我。我们需要和Rust术语做到翻译看齐)
一.总览(特译:https://tutorial.ponylang.io/reference-capabilities.html):
我们已经学了很多有关Pony类型系统的知识,之后是表达式语法,在有关引用能力的这章里,我们会学到另一Pony类型系统组件级别的知识点。目前好像没有一款主流编程语言支持这一特性。那么什么是引用能力?
Pony语言还支持一种不同的能力——引用能力。对象能力(object capabilities)允许你在对象上作些文章,引用能力却阻止你为了做更多事而申请使用更多内存。比如说,你可以使用这块内存但仅仅是为了读取它,而不是更改它。这就是引用能力,它拒绝操作的权限。
引用能力是使得Pony语言与众不同的核心。在简介里,你或许读到过Pony的优点:
而引用能力就是Pony语言拿来实现上述这些超赞的特性的。不要害怕,咱们现在开始吧!
官方指导:https://www.ponylang.io/learn/#reference-capabilities。你可以里面提到的网址进行广泛的对象能力的理解性学习
二、引用能力
(一).基础概念
虽然其中有些你可能已经熟能成诵了,但为了完整性,还是要啰嗦几句:
1.难以创建能分享的可变数据(Shared mutable data is hard):
在并发开发中,最需重视的问题就是可变数据。设想有两个不同的线程均有权访问某一块数据并且都想更新它(同时!)。最好的情况是,它们两个各有一份不同版本的数据,而最坏的情况是它们都在试图更新一块garbage。最标准的方法(主流)是使用锁来防止数据写入同时发生。这种情况下就导致性能大大降低且作为使用者难以获得权限,从而引出无数bug!
2.不变数据可以被安全分享(Immutable data can be safely shared):
既然数据是不变的,那么也就是说永远不会更新,从而也就避免了由更新引发的并发性问题。
3.孤立数据是安全的(Isolated data is safe):
如果一块数据只有一个引用我们就叫它是孤立的(isolated)(注:0引用如果处理的好的话学名叫垃圾)。既然只有对此只有一个引用,孤立数据就不可能被多个线程分享(此处类比一夫一妻制),所以也不存在并发性问题。
4.孤立数据可能很复杂(Isolated data may be complex):
所谓的孤立数据可能是指一个字节,抑或是一大块带很多引用的数据结构。而我们就看这块数据能不能被当成一个整体,如果能,我们就可以把它设置成isolated。当然,还有一个孤立边界(isolation boundary)需要了解。isolated数据满足:
- 在孤立边界外必须有一个对于边界内的对象的引用。
- 对于边界内,可以有任意数量的引用,但都不能指向边界外的对象。
5.每个actor都是一个独立线程(Every actor is single threaded):
只含有一个actor的代码永远不会并发运行。只有我们想要在actor间分享数据时才可能出现问题。
(二)类型修饰符:
在C/C++中,你可能使用过const来修饰类型,你也见过某些场景让你说出int const和const int的区别。const在那里就是类型修饰符。在Pony的引用能力实现中,我们使用类型修饰符的方法。在数组那节我们见到了Array[T] ref 和Array[T] val。下面我们就来看看具体实现。
(三)引用能力列表:
- Isolated,写作 iso,表示孤立数据结构。如果你有一个iso变量,你就知道没有其他变量可以访问这份数据(相当于不存在其他引用,包括二次及以上引用)。因此你可以随意更改它,最后把它交给另一个actor(之后意味着你放弃了这个引用的所有权,把它送给了另一个actor)。
- Value,写作 val,表示那些不可变的数据结构。如果你有一个val 变量,你就知道没有人可以改变它的数据。从而你可以随便读它的数据并和其他actor分享(区别分享和送给,分享意味着你保留了引用的所有权)。
- Reference,写作 ref,表示可变的数据结构并且是非孤立的,换句话说,就是普通的数据。如果你有一个ref 变量,你就知道你可以随意进行读写,且很多变量都可以访问到同样的数据(你不能同时读和写,但别人可以同时读)。但你不能和其他actor分享。
- Box,写作box,表示那些对你来说是只读的变量。这些数据可能是不变的,与其他actor共享的或者说有其他变量可以改变它的数据(打个比方,可以是一个actor有一个reference,你有一个box,特别像服务器和客户端)。box 变量可以被用于安全地读取数据。可能听上去没什么意义,但它允许你为val 和 ref 变量写出同样的代码(毕竟Pony并不支持重载),只要代码不写入数据。
- Transition,写作 trn,表示当你同一时间拥有对某一数据的box引用,还想向其中写入数据。当然了,你也可以把trn 变量转换成val 变量,从而保证可以被安全地传递到其他actor。
- Tag,写作tag,表示那些只是用来当标识的变量。你既不能读也不能写,但你可以储存或比较不同的tag 变量(了解Erlang / Elixir的读者可能联想到原子,想到这里非常好。对那些不熟悉原子概念的读者,我挑了一篇写的比较好的erlang原子介绍:https://blog.csdn.net/cloveses/article/details/77945856)。
引用能力的书写:
String iso // An isolated string
String trn // A transition string
String ref // A string reference
String val // A string value
String box // A string box
String tag // A string tag
延伸阅读:
如何创建不同引用能力的对象:
后面我们会学到一个更好的方法:复原能力(Recovering Capabilities)
阅读任务:https://tutorial.ponylang.io/reference-capabilities/guarantees.html
三、耗散和解构读取(Consume and Destructive Read):
(一)耗散:
在日常编码中,把一个变量移动到另一个变量很是常见,换句话说,就是给数据改了个名。在Pony里,我们使用consume关键字,把数据从变量的壳子里抽出来,放到另一个变量里,此时原先的那个变量也就仅仅是个空壳:
fun test(a: Wombat iso) =>
var b: Wombat iso = consume a // Allowed!
var c: Wombat tag = a // Not allowed!
那么我能耗散一个字段(field)吗?NO!耗散变量意味着它会成为空壳。你没有办法确保是否有其它别名会访问这块字段。我们使用解构读取来解决这个问题。
(二)解构读取:
我们之前提到过,Pony中的赋值语句会返回变量的旧值(记得吗,万物皆表达式,赋值语句也是表达式,所以它也要返回),这就叫解构读取(destructive read):
class Aardvark
var buddy: Wombat iso
new create() =>
buddy = recover Wombat end
fun ref test(a: Wombat iso) =>
var b: Wombat iso = buddy = consume a // Allowed!
四、复原能力(Recovering Capabilities):
recover表达式允许你提升引用能力。一个可变的引用能力(iso trn ref)可以成为任意引用能力,而一个不可变的引用能力(val box)可以成为任何一个不可变的或不透明的(tag)引用能力。
(一)哪里有用?:
recover表达式最直接的应用就是获得一个你可以传给其他actor的 iso 变量。但它还可以用来做其他事情,比如:
- 创建一个周期的不变数据结构,也就是,你在recover表达式里创造了一个复杂的可变数据结构后,将ref 型结果提升到val 型。
- 将一个iso 变量作为ref 借用过来,对其做一系列可变的复杂操作,最后以iso 形式返回。
- 从一个iso 解压出一个可变字段最后又以iso 形式返回
(二)接收方自动复原:
当你有一个iso或trn型的接收方时,你通常不能调用ref 型的方法。这是因为接收方也是方法的一个参数,这意味着,函数体和调用方可以同时访问接收方。同时这也意味着,你需要给receiver换个名,iso型 => tag型,trn型 => box型,两种结果类型均不是ref的子类型。
但是有一个办法可以绕过这样繁琐的步骤。如果方法所有的参数相对于调用方是可发送的(sendable),那么这个方法的返回值不是 可发送的 就是 不被调用方使用的, 于是我们就可以“自动复原”接收方。
五、假名(Aliasing):
Aliasing means having more than one reference to the same object, within the same actor. This can be the case for a variable or a field.
在Pony里,我们可以为某些引用能力加假名,但不是全部。
(一)假名和拒绝保护(Aliasing and deny guarantees):
如果我们尝试为一个iso变量加假名,我们就破坏了iso引用能力的原则——只有一个引用:
fun test(a: Wombat iso) =>
var b: Wombat iso = a // Not allowed!
按上文讲,iso => tag, trn => box,那么其他的引用能力呢?
在以下三种情况下你需要假名:
- 当你给你一个变量或字段赋值时
- 当你把值当作参数传给方法时
- 当你调用一个方法时,接收方调用的假名就被创建了。它和this对函数体有同样的权限。
(二)临时类型(Ephemeral types):
在Pony里,万物皆表达式,而表达式便就有值,那么cosume a的值是什么?事实上,它不是a的类型,而是a的临时类型。为了区别,我们在之后加一个^表示临时:
fun test(a: Wombat iso): Wombat iso^ =>
consume a
(三)假名类型:
An alias type is a way of saying “whatever we can safely alias this thing as”.
类型A=> 假名类型 A!
fun test(a: A) =>
var b: A! = a
//Here, we’re using A as a type variable, which we’ll cover later.
//So A! means “an alias of whatever type A is”.
阅读任务:传递与分享引用(Passing and Sharing References)、能力子类型(Capability Subtyping)、组合能力(Combining Capabilities)
六、箭头类型(视角)(Arrow Types aka Viewpoints):
(一)使用this->视角:
例如:
class Wombat
var _friend: Wombat
fun friend(): this->Wombat => _friend
在组合能力里我们学到了源自box 的ref / val 返回一个box。如果我们想让上面这个类智能地返回对象,即接收方是ref时就返回ref,val亦如此。我们使用this->来实现。
(二)使用类型参数视角(Using a type parameter as a viewpoint):
(建议先学完泛型再回来看)来自标准库的例子:
class ListValues[A, N: ListNode[A] box] is Iterator[N->A]
这里,N被限制为是ListNode[A] box的子类型,同时这个类还实现了一个接口Iterator,使得我们可以迭代类型N -> A 的值
(三)使用box->视角:
同样来自标准库的例子:
interface Comparable[A: Comparable[A] box]
fun eq(that: box->A): Bool => this is that
fun ne(that: box->A): Bool => not eq(that)
这意味着不论A指向什么,我们只需要读取that。
阅读任务:引用能力表
边栏推荐
- FPGA工程师面试试题集锦21~30
- Talk about API Management - Open Source Edition to SaaS Edition
- Mysql CDC (2.1.1) inital snapshot database set up five concurrent degree, se
- pytorch 学习
- [Thesis Notes] Prototypical Contrast Adaptation for Domain Adaptive Semantic Segmentation
- SEO搜索引擎优化
- Buu Web
- 树莓派入门(3)树莓派GPIO学习
- Joomla漏洞复现
- canvas canvas drawing clock
猜你喜欢
How does Jenkins play with interface automation testing?
k-近邻实现手写数字识别
How to use Apifox's Smart Mock function?
MySQL simple tutorial
一篇文章掌握整个JVM,JVM超详细解析!!!
CORS跨域资源共享漏洞的原理与挖掘方法
【LeetCode】41、 缺失的第一个正数
How to simulate the background API call scene, very detailed!
Interface documentation evolution illustration, some ancient interface documentation tools, you may not have used it
awk of the Three Musketeers of Shell Programming
随机推荐
接口调试还能这么玩?
大佬们,mysql cdc(2.2.1跟之前的版本)从savepoint起有时出现这种情况,有没有什
如何用Apifox 的智能Mock功能?
FPGA工程师面试试题集锦31~40
树莓派入门(3)树莓派GPIO学习
[Thesis Notes] Prototypical Contrast Adaptation for Domain Adaptive Semantic Segmentation
Why are negative numbers in binary represented in two's complement form - binary addition and subtraction
每周推荐短视频:探索AI的应用边界
Mysql CDC (2.1.1) inital snapshot database set up five concurrent degree, se
pytorch 学习
MySql之json_extract函数处理json字段
【LeetCode】41、 缺失的第一个正数
FPGA工程师面试试题集锦21~30
聊聊 API 管理-开源版 到 SaaS 版
线性模型中的高级特征选择技术——基于R
An article will help you understand what is idempotency?How to solve the idempotency problem?
Stacks and Queues | Implementing Queues with Stacks | Implementing Stacks with Queues | Basic Theory and Code Principles
AVL树的插入--旋转笔记
转型做产品,考NPDP靠谱吗?
Order table delete, insert and search operations